diff options
Diffstat (limited to 'drivers')
208 files changed, 5658 insertions, 1813 deletions
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index 07e20135f01..0bba148a2c6 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -139,7 +139,7 @@ acpi_status acpi_ev_initialize_op_regions(void); acpi_status acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, u32 function, - acpi_physical_address address, + u32 region_offset, u32 bit_width, acpi_integer * value); acpi_status diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 16e5210ae93..3d87362d17e 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -362,9 +362,6 @@ extern u8 acpi_gbl_method_executing; extern u8 acpi_gbl_abort_method; extern u8 acpi_gbl_db_terminate_threads; -ACPI_EXTERN int optind; -ACPI_EXTERN char *optarg; - ACPI_EXTERN u8 acpi_gbl_db_opt_tables; ACPI_EXTERN u8 acpi_gbl_db_opt_stats; ACPI_EXTERN u8 acpi_gbl_db_opt_ini_methods; diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 2ec394a328e..ee986edfa0d 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -205,6 +205,7 @@ struct acpi_namespace_node { #define ANOBJ_METHOD_LOCAL 0x08 /* Node is a method local */ #define ANOBJ_SUBTREE_HAS_INI 0x10 /* Used to optimize device initialization */ #define ANOBJ_EVALUATED 0x20 /* Set on first evaluation of node */ +#define ANOBJ_ALLOCATED_BUFFER 0x40 /* Method AML buffer is dynamic (install_method) */ #define ANOBJ_IS_EXTERNAL 0x08 /* i_aSL only: This object created via External() */ #define ANOBJ_METHOD_NO_RETVAL 0x10 /* i_aSL only: Method has no return value */ @@ -788,11 +789,14 @@ struct acpi_bit_register_info { /* For control registers, both ignored and reserved bits must be preserved */ /* - * The ACPI spec says to ignore PM1_CTL.SCI_EN (bit 0) - * but we need to be able to write ACPI_BITREG_SCI_ENABLE directly - * as a BIOS workaround on some machines. + * For PM1 control, the SCI enable bit (bit 0, SCI_EN) is defined by the + * ACPI specification to be a "preserved" bit - "OSPM always preserves this + * bit position", section 4.7.3.2.1. However, on some machines the OS must + * write a one to this bit after resume for the machine to work properly. + * To enable this, we no longer attempt to preserve this bit. No machines + * are known to fail if the bit is not preserved. (May 2009) */ -#define ACPI_PM1_CONTROL_IGNORED_BITS 0x0200 /* Bits 9 */ +#define ACPI_PM1_CONTROL_IGNORED_BITS 0x0200 /* Bit 9 */ #define ACPI_PM1_CONTROL_RESERVED_BITS 0xC1F8 /* Bits 14-15, 3-8 */ #define ACPI_PM1_CONTROL_PRESERVED_BITS \ (ACPI_PM1_CONTROL_IGNORED_BITS | ACPI_PM1_CONTROL_RESERVED_BITS) diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index 46cb5b46d28..94cdc2b8cb9 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -99,10 +99,19 @@ acpi_ns_walk_namespace(acpi_object_type type, acpi_walk_callback user_function, void *context, void **return_value); -struct acpi_namespace_node *acpi_ns_get_next_node(acpi_object_type type, struct acpi_namespace_node - *parent, struct acpi_namespace_node +struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node + *parent, + struct acpi_namespace_node *child); +struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type, + struct + acpi_namespace_node + *parent, + struct + acpi_namespace_node + *child); + /* * nsparse - table parsing */ diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index ff851c5df69..067f967eb38 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -483,7 +483,7 @@ typedef enum { #define AML_METHOD_ARG_COUNT 0x07 #define AML_METHOD_SERIALIZED 0x08 -#define AML_METHOD_SYNCH_LEVEL 0xF0 +#define AML_METHOD_SYNC_LEVEL 0xF0 /* METHOD_FLAGS_ARG_COUNT is not used internally, define additional flags */ diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c index dab3f48f0b4..02e6caad4a7 100644 --- a/drivers/acpi/acpica/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -734,7 +734,8 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, /* Local ID (0-7) is (AML opcode - base AML_LOCAL_OP) */ - obj_desc->reference.value = opcode - AML_LOCAL_OP; + obj_desc->reference.value = + ((u32)opcode) - AML_LOCAL_OP; obj_desc->reference.class = ACPI_REFCLASS_LOCAL; #ifndef ACPI_NO_METHOD_EXECUTION @@ -754,7 +755,7 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, /* Arg ID (0-6) is (AML opcode - base AML_ARG_OP) */ - obj_desc->reference.value = opcode - AML_ARG_OP; + obj_desc->reference.value = ((u32)opcode) - AML_ARG_OP; obj_desc->reference.class = ACPI_REFCLASS_ARG; #ifndef ACPI_NO_METHOD_EXECUTION diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c index b4c87b5053e..584d766e6f1 100644 --- a/drivers/acpi/acpica/dsopcode.c +++ b/drivers/acpi/acpica/dsopcode.c @@ -1386,14 +1386,19 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state, case AML_BREAK_POINT_OP: - /* Call up to the OS service layer to handle this */ - - status = - acpi_os_signal(ACPI_SIGNAL_BREAKPOINT, - "Executed AML Breakpoint opcode"); + /* + * Set the single-step flag. This will cause the debugger (if present) + * to break to the console within the AML debugger at the start of the + * next AML instruction. + */ + ACPI_DEBUGGER_EXEC(acpi_gbl_cm_single_step = TRUE); + ACPI_DEBUGGER_EXEC(acpi_os_printf + ("**break** Executed AML BreakPoint opcode\n")); - /* If and when it returns, all done. */ + /* Call to the OSL in case OS wants a piece of the action */ + status = acpi_os_signal(ACPI_SIGNAL_BREAKPOINT, + "Executed AML Breakpoint opcode"); break; case AML_BREAK_OP: diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c index 40f92bf7dce..e46c821cf57 100644 --- a/drivers/acpi/acpica/dswstate.c +++ b/drivers/acpi/acpica/dswstate.c @@ -102,7 +102,7 @@ acpi_ds_result_pop(union acpi_operand_object **object, /* Return object of the top element and clean that top element result stack */ walk_state->result_count--; - index = walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM; + index = (u32)walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM; *object = state->results.obj_desc[index]; if (!*object) { @@ -186,7 +186,7 @@ acpi_ds_result_push(union acpi_operand_object * object, /* Assign the address of object to the top free element of result stack */ - index = walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM; + index = (u32)walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM; state->results.obj_desc[index] = object; walk_state->result_count++; diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c index 538d6326455..98c7f9c6265 100644 --- a/drivers/acpi/acpica/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -275,7 +275,7 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function) * * PARAMETERS: region_obj - Internal region object * Function - Read or Write operation - * Address - Where in the space to read or write + * region_offset - Where in the region to read or write * bit_width - Field width in bits (8, 16, 32, or 64) * Value - Pointer to in or out value, must be * full 64-bit acpi_integer @@ -290,7 +290,7 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function) acpi_status acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, u32 function, - acpi_physical_address address, + u32 region_offset, u32 bit_width, acpi_integer * value) { acpi_status status; @@ -396,7 +396,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, "Handler %p (@%p) Address %8.8X%8.8X [%s]\n", ®ion_obj->region.handler->address_space, handler, - ACPI_FORMAT_NATIVE_UINT(address), + ACPI_FORMAT_NATIVE_UINT(region_obj->region.address + + region_offset), acpi_ut_get_region_name(region_obj->region. space_id))); @@ -412,8 +413,9 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, /* Call the handler */ - status = handler(function, address, bit_width, value, - handler_desc->address_space.context, + status = handler(function, + (region_obj->region.address + region_offset), + bit_width, value, handler_desc->address_space.context, region_obj2->extra.region_context); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index d0a080747ec..4721f58fe42 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -51,7 +51,7 @@ ACPI_MODULE_NAME("evxfevnt") /* Local prototypes */ -acpi_status +static acpi_status acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, struct acpi_gpe_block_info *gpe_block, void *context); @@ -785,7 +785,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_gpe_device) * block device. NULL if the GPE is one of the FADT-defined GPEs. * ******************************************************************************/ -acpi_status +static acpi_status acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, struct acpi_gpe_block_info *gpe_block, void *context) { diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 3deb20a126b..277fd609611 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -47,6 +47,7 @@ #include "acnamesp.h" #include "actables.h" #include "acdispat.h" +#include "acevents.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exconfig") @@ -57,6 +58,10 @@ acpi_ex_add_table(u32 table_index, struct acpi_namespace_node *parent_node, union acpi_operand_object **ddb_handle); +static acpi_status +acpi_ex_region_read(union acpi_operand_object *obj_desc, + u32 length, u8 *buffer); + /******************************************************************************* * * FUNCTION: acpi_ex_add_table @@ -91,6 +96,7 @@ acpi_ex_add_table(u32 table_index, /* Init the table handle */ + obj_desc->common.flags |= AOPOBJ_DATA_VALID; obj_desc->reference.class = ACPI_REFCLASS_TABLE; *ddb_handle = obj_desc; @@ -229,6 +235,8 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state, walk_state); if (ACPI_FAILURE(status)) { (void)acpi_ex_unload_table(ddb_handle); + + acpi_ut_remove_reference(ddb_handle); return_ACPI_STATUS(status); } } @@ -254,6 +262,47 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state, /******************************************************************************* * + * FUNCTION: acpi_ex_region_read + * + * PARAMETERS: obj_desc - Region descriptor + * Length - Number of bytes to read + * Buffer - Pointer to where to put the data + * + * RETURN: Status + * + * DESCRIPTION: Read data from an operation region. The read starts from the + * beginning of the region. + * + ******************************************************************************/ + +static acpi_status +acpi_ex_region_read(union acpi_operand_object *obj_desc, u32 length, u8 *buffer) +{ + acpi_status status; + acpi_integer value; + u32 region_offset = 0; + u32 i; + + /* Bytewise reads */ + + for (i = 0; i < length; i++) { + status = acpi_ev_address_space_dispatch(obj_desc, ACPI_READ, + region_offset, 8, + &value); + if (ACPI_FAILURE(status)) { + return status; + } + + *buffer = (u8)value; + buffer++; + region_offset++; + } + + return AE_OK; +} + +/******************************************************************************* + * * FUNCTION: acpi_ex_load_op * * PARAMETERS: obj_desc - Region or Buffer/Field where the table will be @@ -314,18 +363,23 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, } } - /* - * Map the table header and get the actual table length. The region - * length is not guaranteed to be the same as the table length. - */ - table = acpi_os_map_memory(obj_desc->region.address, - sizeof(struct acpi_table_header)); + /* Get the table header first so we can get the table length */ + + table = ACPI_ALLOCATE(sizeof(struct acpi_table_header)); if (!table) { return_ACPI_STATUS(AE_NO_MEMORY); } + status = + acpi_ex_region_read(obj_desc, + sizeof(struct acpi_table_header), + ACPI_CAST_PTR(u8, table)); length = table->length; - acpi_os_unmap_memory(table, sizeof(struct acpi_table_header)); + ACPI_FREE(table); + + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } /* Must have at least an ACPI table header */ @@ -334,10 +388,19 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, } /* - * The memory region is not guaranteed to remain stable and we must - * copy the table to a local buffer. For example, the memory region - * is corrupted after suspend on some machines. Dynamically loaded - * tables are usually small, so this overhead is minimal. + * The original implementation simply mapped the table, with no copy. + * However, the memory region is not guaranteed to remain stable and + * we must copy the table to a local buffer. For example, the memory + * region is corrupted after suspend on some machines. Dynamically + * loaded tables are usually small, so this overhead is minimal. + * + * The latest implementation (5/2009) does not use a mapping at all. + * We use the low-level operation region interface to read the table + * instead of the obvious optimization of using a direct mapping. + * This maintains a consistent use of operation regions across the + * entire subsystem. This is important if additional processing must + * be performed in the (possibly user-installed) operation region + * handler. For example, acpi_exec and ASLTS depend on this. */ /* Allocate a buffer for the table */ @@ -347,17 +410,16 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, return_ACPI_STATUS(AE_NO_MEMORY); } - /* Map the entire table and copy it */ + /* Read the entire table */ - table = acpi_os_map_memory(obj_desc->region.address, length); - if (!table) { + status = acpi_ex_region_read(obj_desc, length, + ACPI_CAST_PTR(u8, + table_desc.pointer)); + if (ACPI_FAILURE(status)) { ACPI_FREE(table_desc.pointer); - return_ACPI_STATUS(AE_NO_MEMORY); + return_ACPI_STATUS(status); } - ACPI_MEMCPY(table_desc.pointer, table, length); - acpi_os_unmap_memory(table, length); - table_desc.address = obj_desc->region.address; break; @@ -454,6 +516,10 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, return_ACPI_STATUS(status); } + /* Remove the reference by added by acpi_ex_store above */ + + acpi_ut_remove_reference(ddb_handle); + /* Invoke table handler if present */ if (acpi_gbl_table_handler) { @@ -495,13 +561,18 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle) /* * Validate the handle - * Although the handle is partially validated in acpi_ex_reconfiguration(), + * Although the handle is partially validated in acpi_ex_reconfiguration() * when it calls acpi_ex_resolve_operands(), the handle is more completely * validated here. + * + * Handle must be a valid operand object of type reference. Also, the + * ddb_handle must still be marked valid (table has not been previously + * unloaded) */ if ((!ddb_handle) || (ACPI_GET_DESCRIPTOR_TYPE(ddb_handle) != ACPI_DESC_TYPE_OPERAND) || - (ddb_handle->common.type != ACPI_TYPE_LOCAL_REFERENCE)) { + (ddb_handle->common.type != ACPI_TYPE_LOCAL_REFERENCE) || + (!(ddb_handle->common.flags & AOPOBJ_DATA_VALID))) { return_ACPI_STATUS(AE_BAD_PARAMETER); } @@ -509,6 +580,12 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle) table_index = table_desc->reference.value; + /* Ensure the table is still loaded */ + + if (!acpi_tb_is_table_loaded(table_index)) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + /* Invoke table handler if present */ if (acpi_gbl_table_handler) { @@ -530,8 +607,10 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle) (void)acpi_tb_release_owner_id(table_index); acpi_tb_set_table_loaded_flag(table_index, FALSE); - /* Table unloaded, remove a reference to the ddb_handle object */ - - acpi_ut_remove_reference(ddb_handle); + /* + * Invalidate the handle. We do this because the handle may be stored + * in a named object and may not be actually deleted until much later. + */ + ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID; return_ACPI_STATUS(AE_OK); } diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c index a57ad2564ab..02b25d233d9 100644 --- a/drivers/acpi/acpica/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -502,7 +502,7 @@ acpi_ex_create_method(u8 * aml_start, * ACPI 2.0: sync_level = sync_level in method declaration */ obj_desc->method.sync_level = (u8) - ((method_flags & AML_METHOD_SYNCH_LEVEL) >> 4); + ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4); } /* Attach the new object to the method Node */ diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c index 89d141fdae0..ec524614e70 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -120,9 +120,11 @@ static struct acpi_exdump_info acpi_ex_dump_event[2] = { {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(event.os_semaphore), "OsSemaphore"} }; -static struct acpi_exdump_info acpi_ex_dump_method[8] = { +static struct acpi_exdump_info acpi_ex_dump_method[9] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_method), NULL}, - {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.param_count), "ParamCount"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.method_flags), "Method Flags"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.param_count), + "Parameter Count"}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.sync_level), "Sync Level"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(method.mutex), "Mutex"}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.owner_id), "Owner Id"}, diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index 99cee61e655..d4075b82102 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -222,7 +222,7 @@ acpi_ex_access_region(union acpi_operand_object *obj_desc, { acpi_status status; union acpi_operand_object *rgn_desc; - acpi_physical_address address; + u32 region_offset; ACPI_FUNCTION_TRACE(ex_access_region); @@ -243,7 +243,7 @@ acpi_ex_access_region(union acpi_operand_object *obj_desc, * 3) The current offset into the field */ rgn_desc = obj_desc->common_field.region_obj; - address = rgn_desc->region.address + + region_offset = obj_desc->common_field.base_byte_offset + field_datum_byte_offset; if ((function & ACPI_IO_MASK) == ACPI_READ) { @@ -260,16 +260,18 @@ acpi_ex_access_region(union acpi_operand_object *obj_desc, obj_desc->common_field.access_byte_width, obj_desc->common_field.base_byte_offset, field_datum_byte_offset, ACPI_CAST_PTR(void, - address))); + (rgn_desc-> + region. + address + + region_offset)))); /* Invoke the appropriate address_space/op_region handler */ - status = acpi_ev_address_space_dispatch(rgn_desc, function, - address, - ACPI_MUL_8(obj_desc-> - common_field. - access_byte_width), - value); + status = + acpi_ev_address_space_dispatch(rgn_desc, function, region_offset, + ACPI_MUL_8(obj_desc->common_field. + access_byte_width), + value); if (ACPI_FAILURE(status)) { if (status == AE_NOT_IMPLEMENTED) { diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c index d301c1f363e..2f0114202b0 100644 --- a/drivers/acpi/acpica/exmutex.c +++ b/drivers/acpi/acpica/exmutex.c @@ -83,6 +83,15 @@ void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc) if (obj_desc->mutex.prev) { (obj_desc->mutex.prev)->mutex.next = obj_desc->mutex.next; + + /* + * Migrate the previous sync level associated with this mutex to the + * previous mutex on the list so that it may be preserved. This handles + * the case where several mutexes have been acquired at the same level, + * but are not released in opposite order. + */ + (obj_desc->mutex.prev)->mutex.original_sync_level = + obj_desc->mutex.original_sync_level; } else { thread->acquired_mutex_list = obj_desc->mutex.next; } @@ -349,6 +358,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, struct acpi_walk_state *walk_state) { acpi_status status = AE_OK; + u8 previous_sync_level; ACPI_FUNCTION_TRACE(ex_release_mutex); @@ -373,11 +383,12 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, walk_state->thread->thread_id) && (obj_desc != acpi_gbl_global_lock_mutex)) { ACPI_ERROR((AE_INFO, - "Thread %lX cannot release Mutex [%4.4s] acquired by thread %lX", - (unsigned long)walk_state->thread->thread_id, + "Thread %p cannot release Mutex [%4.4s] acquired by thread %p", + ACPI_CAST_PTR(void, walk_state->thread->thread_id), acpi_ut_get_node_name(obj_desc->mutex.node), - (unsigned long)obj_desc->mutex.owner_thread-> - thread_id)); + ACPI_CAST_PTR(void, + obj_desc->mutex.owner_thread-> + thread_id))); return_ACPI_STATUS(AE_AML_NOT_OWNER); } @@ -391,10 +402,14 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, } /* - * The sync level of the mutex must be less than or equal to the current - * sync level + * The sync level of the mutex must be equal to the current sync level. In + * other words, the current level means that at least one mutex at that + * level is currently being held. Attempting to release a mutex of a + * different level can only mean that the mutex ordering rule is being + * violated. This behavior is clarified in ACPI 4.0 specification. */ - if (obj_desc->mutex.sync_level > walk_state->thread->current_sync_level) { + if (obj_desc->mutex.sync_level != + walk_state->thread->current_sync_level) { ACPI_ERROR((AE_INFO, "Cannot release Mutex [%4.4s], SyncLevel mismatch: mutex %d current %d", acpi_ut_get_node_name(obj_desc->mutex.node), @@ -403,14 +418,24 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, return_ACPI_STATUS(AE_AML_MUTEX_ORDER); } + /* + * Get the previous sync_level from the head of the acquired mutex list. + * This handles the case where several mutexes at the same level have been + * acquired, but are not released in reverse order. + */ + previous_sync_level = + walk_state->thread->acquired_mutex_list->mutex.original_sync_level; + status = acpi_ex_release_mutex_object(obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } if (obj_desc->mutex.acquisition_depth == 0) { - /* Restore the original sync_level */ + /* Restore the previous sync_level */ - walk_state->thread->current_sync_level = - obj_desc->mutex.original_sync_level; + walk_state->thread->current_sync_level = previous_sync_level; } return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c index 90d606196c9..6efd07a4f77 100644 --- a/drivers/acpi/acpica/exstore.c +++ b/drivers/acpi/acpica/exstore.c @@ -193,10 +193,12 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc, case ACPI_REFCLASS_TABLE: + /* Case for ddb_handle */ + ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, "Table Index 0x%X\n", source_desc->reference.value)); - break; + return; default: break; diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c index 7b2fb602b5c..23d5505cb1f 100644 --- a/drivers/acpi/acpica/hwregs.c +++ b/drivers/acpi/acpica/hwregs.c @@ -81,9 +81,9 @@ acpi_status acpi_hw_clear_acpi_status(void) ACPI_FUNCTION_TRACE(hw_clear_acpi_status); - ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %0llX\n", + ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %8.8X%8.8X\n", ACPI_BITMASK_ALL_FIXED_STATUS, - acpi_gbl_xpm1a_status.address)); + ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address))); lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); diff --git a/drivers/acpi/acpica/nsalloc.c b/drivers/acpi/acpica/nsalloc.c index aceb9311196..efc971ab7d6 100644 --- a/drivers/acpi/acpica/nsalloc.c +++ b/drivers/acpi/acpica/nsalloc.c @@ -334,9 +334,7 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) /* Get the next node in this scope (NULL if none) */ - child_node = - acpi_ns_get_next_node(ACPI_TYPE_ANY, parent_node, - child_node); + child_node = acpi_ns_get_next_node(parent_node, child_node); if (child_node) { /* Found a child node - detach any attached object */ @@ -345,8 +343,7 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) /* Check if this node has any children */ - if (acpi_ns_get_next_node - (ACPI_TYPE_ANY, child_node, NULL)) { + if (child_node->child) { /* * There is at least one child of this node, * visit the node @@ -432,9 +429,7 @@ void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id) * Get the next child of this parent node. When child_node is NULL, * the first child of the parent is returned */ - child_node = - acpi_ns_get_next_node(ACPI_TYPE_ANY, parent_node, - child_node); + child_node = acpi_ns_get_next_node(parent_node, child_node); if (deletion_node) { acpi_ns_delete_children(deletion_node); @@ -452,8 +447,7 @@ void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id) /* Check if this node has any children */ - if (acpi_ns_get_next_node - (ACPI_TYPE_ANY, child_node, NULL)) { + if (child_node->child) { /* * There is at least one child of this node, * visit the node diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c index ae3dc10a7e8..af8e6bcee07 100644 --- a/drivers/acpi/acpica/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -149,7 +149,7 @@ char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node) name_buffer = ACPI_ALLOCATE_ZEROED(size); if (!name_buffer) { - ACPI_ERROR((AE_INFO, "Allocation failure")); + ACPI_ERROR((AE_INFO, "Could not allocate %u bytes", (u32)size)); return_PTR(NULL); } diff --git a/drivers/acpi/acpica/nsobject.c b/drivers/acpi/acpica/nsobject.c index 3eb20bfda9d..60f3af08d28 100644 --- a/drivers/acpi/acpica/nsobject.c +++ b/drivers/acpi/acpica/nsobject.c @@ -213,6 +213,15 @@ void acpi_ns_detach_object(struct acpi_namespace_node *node) return_VOID; } + if (node->flags & ANOBJ_ALLOCATED_BUFFER) { + + /* Free the dynamic aml buffer */ + + if (obj_desc->common.type == ACPI_TYPE_METHOD) { + ACPI_FREE(obj_desc->method.aml_start); + } + } + /* Clear the entry in all cases */ node->object = NULL; diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index d9e8cbc6e67..7f8e066b12a 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -144,7 +144,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, pathname = acpi_ns_get_external_pathname(node); if (!pathname) { - pathname = ACPI_CAST_PTR(char, predefined->info.name); + return AE_OK; /* Could not get pathname, ignore */ } /* @@ -230,10 +230,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, } exit: - if (pathname != predefined->info.name) { - ACPI_FREE(pathname); - } - + ACPI_FREE(pathname); return (status); } diff --git a/drivers/acpi/acpica/nssearch.c b/drivers/acpi/acpica/nssearch.c index f9b4f51bf8f..7e865639a92 100644 --- a/drivers/acpi/acpica/nssearch.c +++ b/drivers/acpi/acpica/nssearch.c @@ -45,6 +45,10 @@ #include "accommon.h" #include "acnamesp.h" +#ifdef ACPI_ASL_COMPILER +#include "amlcode.h" +#endif + #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nssearch") diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c index 83e3aa6d4b9..35539df5c75 100644 --- a/drivers/acpi/acpica/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -52,8 +52,7 @@ ACPI_MODULE_NAME("nswalk") * * FUNCTION: acpi_ns_get_next_node * - * PARAMETERS: Type - Type of node to be searched for - * parent_node - Parent node whose children we are + * PARAMETERS: parent_node - Parent node whose children we are * getting * child_node - Previous child that was found. * The NEXT child will be returned @@ -66,27 +65,68 @@ ACPI_MODULE_NAME("nswalk") * within Scope is returned. * ******************************************************************************/ -struct acpi_namespace_node *acpi_ns_get_next_node(acpi_object_type type, struct acpi_namespace_node - *parent_node, struct acpi_namespace_node +struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node + *parent_node, + struct acpi_namespace_node *child_node) { - struct acpi_namespace_node *next_node = NULL; - ACPI_FUNCTION_ENTRY(); if (!child_node) { /* It's really the parent's _scope_ that we want */ - next_node = parent_node->child; + return parent_node->child; } - else { - /* Start search at the NEXT node */ - - next_node = acpi_ns_get_next_valid_node(child_node); + /* + * Get the next node. + * + * If we are at the end of this peer list, return NULL + */ + if (child_node->flags & ANOBJ_END_OF_PEER_LIST) { + return NULL; } + /* Otherwise just return the next peer */ + + return child_node->peer; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_next_node_typed + * + * PARAMETERS: Type - Type of node to be searched for + * parent_node - Parent node whose children we are + * getting + * child_node - Previous child that was found. + * The NEXT child will be returned + * + * RETURN: struct acpi_namespace_node - Pointer to the NEXT child or NULL if + * none is found. + * + * DESCRIPTION: Return the next peer node within the namespace. If Handle + * is valid, Scope is ignored. Otherwise, the first node + * within Scope is returned. + * + ******************************************************************************/ + +struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type, + struct + acpi_namespace_node + *parent_node, + struct + acpi_namespace_node + *child_node) +{ + struct acpi_namespace_node *next_node = NULL; + + ACPI_FUNCTION_ENTRY(); + + next_node = acpi_ns_get_next_node(parent_node, child_node); + + /* If any type is OK, we are done */ if (type == ACPI_TYPE_ANY) { @@ -186,9 +226,7 @@ acpi_ns_walk_namespace(acpi_object_type type, /* Get the next node in this scope. Null if not found */ status = AE_OK; - child_node = - acpi_ns_get_next_node(ACPI_TYPE_ANY, parent_node, - child_node); + child_node = acpi_ns_get_next_node(parent_node, child_node); if (child_node) { /* Found next child, get the type if we are not searching for ANY */ @@ -269,8 +307,7 @@ acpi_ns_walk_namespace(acpi_object_type type, * function has specified that the maximum depth has been reached. */ if ((level < max_depth) && (status != AE_CTRL_DEPTH)) { - if (acpi_ns_get_next_node - (ACPI_TYPE_ANY, child_node, NULL)) { + if (child_node->child) { /* There is at least one child of this node, visit it */ diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index 9589fea2499..f23593d6add 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -45,6 +45,8 @@ #include <acpi/acpi.h> #include "accommon.h" #include "acnamesp.h" +#include "acparser.h" +#include "amlcode.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsxfname") @@ -358,3 +360,151 @@ acpi_get_object_info(acpi_handle handle, struct acpi_buffer * buffer) } ACPI_EXPORT_SYMBOL(acpi_get_object_info) + +/****************************************************************************** + * + * FUNCTION: acpi_install_method + * + * PARAMETERS: Buffer - An ACPI table containing one control method + * + * RETURN: Status + * + * DESCRIPTION: Install a control method into the namespace. If the method + * name already exists in the namespace, it is overwritten. The + * input buffer must contain a valid DSDT or SSDT containing a + * single control method. + * + ******************************************************************************/ +acpi_status acpi_install_method(u8 *buffer) +{ + struct acpi_table_header *table = + ACPI_CAST_PTR(struct acpi_table_header, buffer); + u8 *aml_buffer; + u8 *aml_start; + char *path; + struct acpi_namespace_node *node; + union acpi_operand_object *method_obj; + struct acpi_parse_state parser_state; + u32 aml_length; + u16 opcode; + u8 method_flags; + acpi_status status; + + /* Parameter validation */ + + if (!buffer) { + return AE_BAD_PARAMETER; + } + + /* Table must be a DSDT or SSDT */ + + if (!ACPI_COMPARE_NAME(table->signature, ACPI_SIG_DSDT) && + !ACPI_COMPARE_NAME(table->signature, ACPI_SIG_SSDT)) { + return AE_BAD_HEADER; + } + + /* First AML opcode in the table must be a control method */ + + parser_state.aml = buffer + sizeof(struct acpi_table_header); + opcode = acpi_ps_peek_opcode(&parser_state); + if (opcode != AML_METHOD_OP) { + return AE_BAD_PARAMETER; + } + + /* Extract method information from the raw AML */ + + parser_state.aml += acpi_ps_get_opcode_size(opcode); + parser_state.pkg_end = acpi_ps_get_next_package_end(&parser_state); + path = acpi_ps_get_next_namestring(&parser_state); + method_flags = *parser_state.aml++; + aml_start = parser_state.aml; + aml_length = ACPI_PTR_DIFF(parser_state.pkg_end, aml_start); + + /* + * Allocate resources up-front. We don't want to have to delete a new + * node from the namespace if we cannot allocate memory. + */ + aml_buffer = ACPI_ALLOCATE(aml_length); + if (!aml_buffer) { + return AE_NO_MEMORY; + } + + method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD); + if (!method_obj) { + ACPI_FREE(aml_buffer); + return AE_NO_MEMORY; + } + + /* Lock namespace for acpi_ns_lookup, we may be creating a new node */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + /* The lookup either returns an existing node or creates a new one */ + + status = + acpi_ns_lookup(NULL, path, ACPI_TYPE_METHOD, ACPI_IMODE_LOAD_PASS1, + ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND, + NULL, &node); + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + + if (ACPI_FAILURE(status)) { /* ns_lookup */ + if (status != AE_ALREADY_EXISTS) { + goto error_exit; + } + + /* Node existed previously, make sure it is a method node */ + + if (node->type != ACPI_TYPE_METHOD) { + status = AE_TYPE; + goto error_exit; + } + } + + /* Copy the method AML to the local buffer */ + + ACPI_MEMCPY(aml_buffer, aml_start, aml_length); + + /* Initialize the method object with the new method's information */ + + method_obj->method.aml_start = aml_buffer; + method_obj->method.aml_length = aml_length; + + method_obj->method.param_count = (u8) + (method_flags & AML_METHOD_ARG_COUNT); + + method_obj->method.method_flags = (u8) + (method_flags & ~AML_METHOD_ARG_COUNT); + + if (method_flags & AML_METHOD_SERIALIZED) { + method_obj->method.sync_level = (u8) + ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4); + } + + /* + * Now that it is complete, we can attach the new method object to + * the method Node (detaches/deletes any existing object) + */ + status = acpi_ns_attach_object(node, method_obj, ACPI_TYPE_METHOD); + + /* + * Flag indicates AML buffer is dynamic, must be deleted later. + * Must be set only after attach above. + */ + node->flags |= ANOBJ_ALLOCATED_BUFFER; + + /* Remove local reference to the method object */ + + acpi_ut_remove_reference(method_obj); + return status; + +error_exit: + + ACPI_FREE(aml_buffer); + ACPI_FREE(method_obj); + return status; +} +ACPI_EXPORT_SYMBOL(acpi_install_method) diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c index 1c7efc15225..4071bad4458 100644 --- a/drivers/acpi/acpica/nsxfobj.c +++ b/drivers/acpi/acpica/nsxfobj.c @@ -162,6 +162,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_type) acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle) { struct acpi_namespace_node *node; + struct acpi_namespace_node *parent_node; acpi_status status; if (!ret_handle) { @@ -189,12 +190,12 @@ acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle) /* Get the parent entry */ - *ret_handle = - acpi_ns_convert_entry_to_handle(acpi_ns_get_parent_node(node)); + parent_node = acpi_ns_get_parent_node(node); + *ret_handle = acpi_ns_convert_entry_to_handle(parent_node); /* Return exception if parent is null */ - if (!acpi_ns_get_parent_node(node)) { + if (!parent_node) { status = AE_NULL_ENTRY; } @@ -268,7 +269,7 @@ acpi_get_next_object(acpi_object_type type, /* Internal function does the real work */ - node = acpi_ns_get_next_node(type, parent_node, child_node); + node = acpi_ns_get_next_node_typed(type, parent_node, child_node); if (!node) { status = AE_NOT_FOUND; goto unlock_and_exit; diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c index 88b5a2c4814..3c4dcc3d106 100644 --- a/drivers/acpi/acpica/rscalc.c +++ b/drivers/acpi/acpica/rscalc.c @@ -547,7 +547,7 @@ acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object, if (!package_element || (package_element->common.type != ACPI_TYPE_PACKAGE)) { - return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } /* @@ -593,9 +593,6 @@ acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object, } else { temp_size_needed += acpi_ns_get_pathname_length((*sub_object_list)->reference.node); - if (!temp_size_needed) { - return_ACPI_STATUS(AE_BAD_PARAMETER); - } } } else { /* diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index 69a2aa5b5d8..395212bcd19 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -338,13 +338,17 @@ acpi_resource_to_address64(struct acpi_resource *resource, switch (resource->type) { case ACPI_RESOURCE_TYPE_ADDRESS16: - address16 = (struct acpi_resource_address16 *)&resource->data; + address16 = + ACPI_CAST_PTR(struct acpi_resource_address16, + &resource->data); ACPI_COPY_ADDRESS(out, address16); break; case ACPI_RESOURCE_TYPE_ADDRESS32: - address32 = (struct acpi_resource_address32 *)&resource->data; + address32 = + ACPI_CAST_PTR(struct acpi_resource_address32, + &resource->data); ACPI_COPY_ADDRESS(out, address32); break; diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index 71e655d14cb..82b02dcb942 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -284,9 +284,9 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) if (length > sizeof(struct acpi_table_fadt)) { ACPI_WARNING((AE_INFO, "FADT (revision %u) is longer than ACPI 2.0 version, " - "truncating length 0x%X to 0x%zX", - table->revision, (unsigned)length, - sizeof(struct acpi_table_fadt))); + "truncating length 0x%X to 0x%X", + table->revision, length, + (u32)sizeof(struct acpi_table_fadt))); } /* Clear the entire local FADT */ @@ -441,7 +441,7 @@ static void acpi_tb_convert_fadt(void) &acpi_gbl_FADT, fadt_info_table [i].length), - address32); + (u64) address32); } } } @@ -469,7 +469,6 @@ static void acpi_tb_convert_fadt(void) static void acpi_tb_validate_fadt(void) { char *name; - u32 *address32; struct acpi_generic_address *address64; u8 length; u32 i; @@ -505,15 +504,12 @@ static void acpi_tb_validate_fadt(void) for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) { /* - * Generate pointers to the 32-bit and 64-bit addresses, get the - * register length (width), and the register name + * Generate pointer to the 64-bit address, get the register + * length (width) and the register name */ address64 = ACPI_ADD_PTR(struct acpi_generic_address, &acpi_gbl_FADT, fadt_info_table[i].address64); - address32 = - ACPI_ADD_PTR(u32, &acpi_gbl_FADT, - fadt_info_table[i].address32); length = *ACPI_ADD_PTR(u8, &acpi_gbl_FADT, fadt_info_table[i].length); diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index f865d5a096d..63e82329a9e 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -472,7 +472,7 @@ acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index) * lock may block, and also since the execution of a namespace walk * must be allowed to use the interpreter. */ - acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); + (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock); acpi_ns_delete_namespace_by_owner(owner_id); diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c index 919624f123d..0f0c64bf8ac 100644 --- a/drivers/acpi/acpica/utcopy.c +++ b/drivers/acpi/acpica/utcopy.c @@ -676,6 +676,7 @@ acpi_ut_copy_simple_object(union acpi_operand_object *source_desc, { u16 reference_count; union acpi_operand_object *next_object; + acpi_status status; /* Save fields from destination that we don't want to overwrite */ @@ -768,6 +769,28 @@ acpi_ut_copy_simple_object(union acpi_operand_object *source_desc, } break; + /* + * For Mutex and Event objects, we cannot simply copy the underlying + * OS object. We must create a new one. + */ + case ACPI_TYPE_MUTEX: + + status = acpi_os_create_mutex(&dest_desc->mutex.os_mutex); + if (ACPI_FAILURE(status)) { + return status; + } + break; + + case ACPI_TYPE_EVENT: + + status = acpi_os_create_semaphore(ACPI_NO_UNIT_LIMIT, 0, + &dest_desc->event. + os_semaphore); + if (ACPI_FAILURE(status)) { + return status; + } + break; + default: /* Nothing to do for other simple objects */ break; diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index 38821f53042..527d729f681 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -179,9 +179,9 @@ acpi_debug_print(u32 requested_debug_level, if (thread_id != acpi_gbl_prev_thread_id) { if (ACPI_LV_THREADS & acpi_dbg_level) { acpi_os_printf - ("\n**** Context Switch from TID %lX to TID %lX ****\n\n", - (unsigned long)acpi_gbl_prev_thread_id, - (unsigned long)thread_id); + ("\n**** Context Switch from TID %p to TID %p ****\n\n", + ACPI_CAST_PTR(void, acpi_gbl_prev_thread_id), + ACPI_CAST_PTR(void, thread_id)); } acpi_gbl_prev_thread_id = thread_id; @@ -194,7 +194,7 @@ acpi_debug_print(u32 requested_debug_level, acpi_os_printf("%8s-%04ld ", module_name, line_number); if (ACPI_LV_THREADS & acpi_dbg_level) { - acpi_os_printf("[%04lX] ", (unsigned long)thread_id); + acpi_os_printf("[%p] ", ACPI_CAST_PTR(void, thread_id)); } acpi_os_printf("[%02ld] %-22.22s: ", diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index a5ee23bc4f5..bc171031508 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -75,6 +75,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) union acpi_operand_object *handler_desc; union acpi_operand_object *second_desc; union acpi_operand_object *next_desc; + union acpi_operand_object **last_obj_ptr; ACPI_FUNCTION_TRACE_PTR(ut_delete_internal_obj, object); @@ -223,6 +224,26 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) */ handler_desc = object->region.handler; if (handler_desc) { + next_desc = + handler_desc->address_space.region_list; + last_obj_ptr = + &handler_desc->address_space.region_list; + + /* Remove the region object from the handler's list */ + + while (next_desc) { + if (next_desc == object) { + *last_obj_ptr = + next_desc->region.next; + break; + } + + /* Walk the linked list of handler */ + + last_obj_ptr = &next_desc->region.next; + next_desc = next_desc->region.next; + } + if (handler_desc->address_space.handler_flags & ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) { diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c index 1c9e250caef..fbe782348b0 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -1033,11 +1033,12 @@ acpi_error(const char *module_name, u32 line_number, const char *format, ...) { va_list args; - acpi_os_printf("ACPI Error (%s-%04d): ", module_name, line_number); + acpi_os_printf("ACPI Error: "); va_start(args, format); acpi_os_vprintf(format, args); - acpi_os_printf(" [%X]\n", ACPI_CA_VERSION); + acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name, + line_number); va_end(args); } @@ -1047,12 +1048,12 @@ acpi_exception(const char *module_name, { va_list args; - acpi_os_printf("ACPI Exception (%s-%04d): %s, ", module_name, - line_number, acpi_format_exception(status)); + acpi_os_printf("ACPI Exception: %s, ", acpi_format_exception(status)); va_start(args, format); acpi_os_vprintf(format, args); - acpi_os_printf(" [%X]\n", ACPI_CA_VERSION); + acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name, + line_number); va_end(args); } @@ -1061,11 +1062,12 @@ acpi_warning(const char *module_name, u32 line_number, const char *format, ...) { va_list args; - acpi_os_printf("ACPI Warning (%s-%04d): ", module_name, line_number); + acpi_os_printf("ACPI Warning: "); va_start(args, format); acpi_os_vprintf(format, args); - acpi_os_printf(" [%X]\n", ACPI_CA_VERSION); + acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name, + line_number); va_end(args); } @@ -1074,10 +1076,6 @@ acpi_info(const char *module_name, u32 line_number, const char *format, ...) { va_list args; - /* - * Removed module_name, line_number, and acpica version, not needed - * for info output - */ acpi_os_printf("ACPI: "); va_start(args, format); diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index 26c93a748e6..80bb6515411 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -230,17 +230,18 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) if (acpi_gbl_mutex_info[i].thread_id == this_thread_id) { if (i == mutex_id) { ACPI_ERROR((AE_INFO, - "Mutex [%s] already acquired by this thread [%X]", + "Mutex [%s] already acquired by this thread [%p]", acpi_ut_get_mutex_name (mutex_id), - this_thread_id)); + ACPI_CAST_PTR(void, + this_thread_id))); return (AE_ALREADY_ACQUIRED); } ACPI_ERROR((AE_INFO, - "Invalid acquire order: Thread %X owns [%s], wants [%s]", - this_thread_id, + "Invalid acquire order: Thread %p owns [%s], wants [%s]", + ACPI_CAST_PTR(void, this_thread_id), acpi_ut_get_mutex_name(i), acpi_ut_get_mutex_name(mutex_id))); @@ -251,24 +252,24 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) #endif ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %lX attempting to acquire Mutex [%s]\n", - (unsigned long)this_thread_id, + "Thread %p attempting to acquire Mutex [%s]\n", + ACPI_CAST_PTR(void, this_thread_id), acpi_ut_get_mutex_name(mutex_id))); status = acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex, ACPI_WAIT_FOREVER); if (ACPI_SUCCESS(status)) { ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %lX acquired Mutex [%s]\n", - (unsigned long)this_thread_id, + "Thread %p acquired Mutex [%s]\n", + ACPI_CAST_PTR(void, this_thread_id), acpi_ut_get_mutex_name(mutex_id))); acpi_gbl_mutex_info[mutex_id].use_count++; acpi_gbl_mutex_info[mutex_id].thread_id = this_thread_id; } else { ACPI_EXCEPTION((AE_INFO, status, - "Thread %lX could not acquire Mutex [%X]", - (unsigned long)this_thread_id, mutex_id)); + "Thread %p could not acquire Mutex [%X]", + ACPI_CAST_PTR(void, this_thread_id), mutex_id)); } return (status); @@ -293,9 +294,8 @@ acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id) ACPI_FUNCTION_NAME(ut_release_mutex); this_thread_id = acpi_os_get_thread_id(); - ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %lX releasing Mutex [%s]\n", - (unsigned long)this_thread_id, + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %p releasing Mutex [%s]\n", + ACPI_CAST_PTR(void, this_thread_id), acpi_ut_get_mutex_name(mutex_id))); if (mutex_id > ACPI_MAX_MUTEX) { diff --git a/drivers/base/core.c b/drivers/base/core.c index 1977d4beb89..7ecb1938e59 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -22,6 +22,7 @@ #include <linux/kallsyms.h> #include <linux/semaphore.h> #include <linux/mutex.h> +#include <linux/async.h> #include "base.h" #include "power/power.h" @@ -161,10 +162,18 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, struct device *dev = to_dev(kobj); int retval = 0; - /* add the major/minor if present */ + /* add device node properties if present */ if (MAJOR(dev->devt)) { + const char *tmp; + const char *name; + add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt)); add_uevent_var(env, "MINOR=%u", MINOR(dev->devt)); + name = device_get_nodename(dev, &tmp); + if (name) { + add_uevent_var(env, "DEVNAME=%s", name); + kfree(tmp); + } } if (dev->type && dev->type->name) @@ -874,7 +883,7 @@ int device_add(struct device *dev) * the name, and force the use of dev_name() */ if (dev->init_name) { - dev_set_name(dev, dev->init_name); + dev_set_name(dev, "%s", dev->init_name); dev->init_name = NULL; } @@ -1128,6 +1137,47 @@ static struct device *next_device(struct klist_iter *i) } /** + * device_get_nodename - path of device node file + * @dev: device + * @tmp: possibly allocated string + * + * Return the relative path of a possible device node. + * Non-default names may need to allocate a memory to compose + * a name. This memory is returned in tmp and needs to be + * freed by the caller. + */ +const char *device_get_nodename(struct device *dev, const char **tmp) +{ + char *s; + + *tmp = NULL; + + /* the device type may provide a specific name */ + if (dev->type && dev->type->nodename) + *tmp = dev->type->nodename(dev); + if (*tmp) + return *tmp; + + /* the class may provide a specific name */ + if (dev->class && dev->class->nodename) + *tmp = dev->class->nodename(dev); + if (*tmp) + return *tmp; + + /* return name without allocation, tmp == NULL */ + if (strchr(dev_name(dev), '!') == NULL) + return dev_name(dev); + + /* replace '!' in the name with '/' */ + *tmp = kstrdup(dev_name(dev), GFP_KERNEL); + if (!*tmp) + return NULL; + while ((s = strchr(*tmp, '!'))) + s[0] = '/'; + return *tmp; +} + +/** * device_for_each_child - device child iterator. * @parent: parent struct device. * @data: data for the callback. @@ -1271,7 +1321,7 @@ struct device *__root_device_register(const char *name, struct module *owner) if (!root) return ERR_PTR(err); - err = dev_set_name(&root->dev, name); + err = dev_set_name(&root->dev, "%s", name); if (err) { kfree(root); return ERR_PTR(err); @@ -1665,4 +1715,5 @@ void device_shutdown(void) kobject_put(sysfs_dev_char_kobj); kobject_put(sysfs_dev_block_kobj); kobject_put(dev_kobj); + async_synchronize_full(); } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 742cbe6b042..f0106875f01 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -226,7 +226,7 @@ static int __device_attach(struct device_driver *drv, void *data) * pair is found, break out and return. * * Returns 1 if the device was bound to a driver; - * 0 if no matching device was found; + * 0 if no matching driver was found; * -ENODEV if the device is not registered. * * When called for a USB interface, @dev->parent->sem must be held. @@ -320,6 +320,10 @@ static void __device_release_driver(struct device *dev) devres_release_all(dev); dev->driver = NULL; klist_remove(&dev->p->knode_driver); + if (dev->bus) + blocking_notifier_call_chain(&dev->bus->p->bus_notifier, + BUS_NOTIFY_UNBOUND_DRIVER, + dev); } } diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 8a267c42762..ddeb819c8f8 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -40,7 +40,7 @@ static int loading_timeout = 60; /* In seconds */ static DEFINE_MUTEX(fw_lock); struct firmware_priv { - char fw_id[FIRMWARE_NAME_MAX]; + char *fw_id; struct completion completion; struct bin_attribute attr_data; struct firmware *fw; @@ -355,8 +355,9 @@ static void fw_dev_release(struct device *dev) for (i = 0; i < fw_priv->nr_pages; i++) __free_page(fw_priv->pages[i]); kfree(fw_priv->pages); + kfree(fw_priv->fw_id); kfree(fw_priv); - kfree(dev); + put_device(dev); module_put(THIS_MODULE); } @@ -386,13 +387,19 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, init_completion(&fw_priv->completion); fw_priv->attr_data = firmware_attr_data_tmpl; - strlcpy(fw_priv->fw_id, fw_name, FIRMWARE_NAME_MAX); + fw_priv->fw_id = kstrdup(fw_name, GFP_KERNEL); + if (!fw_priv->fw_id) { + dev_err(device, "%s: Firmware name allocation failed\n", + __func__); + retval = -ENOMEM; + goto error_kfree; + } fw_priv->timeout.function = firmware_class_timeout; fw_priv->timeout.data = (u_long) fw_priv; init_timer(&fw_priv->timeout); - dev_set_name(f_dev, dev_name(device)); + dev_set_name(f_dev, "%s", dev_name(device)); f_dev->parent = device; f_dev->class = &firmware_class; dev_set_drvdata(f_dev, fw_priv); @@ -400,14 +407,17 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, retval = device_register(f_dev); if (retval) { dev_err(device, "%s: device_register failed\n", __func__); - goto error_kfree; + put_device(f_dev); + goto error_kfree_fw_id; } *dev_p = f_dev; return 0; +error_kfree_fw_id: + kfree(fw_priv->fw_id); error_kfree: - kfree(fw_priv); kfree(f_dev); + kfree(fw_priv); return retval; } @@ -615,8 +625,9 @@ request_firmware_work_func(void *arg) * @cont: function will be called asynchronously when the firmware * request is over. * - * Asynchronous variant of request_firmware() for contexts where - * it is not possible to sleep. + * Asynchronous variant of request_firmware() for user contexts where + * it is not possible to sleep for long time. It can't be called + * in atomic contexts. **/ int request_firmware_nowait( diff --git a/drivers/base/platform.c b/drivers/base/platform.c index ead3f64c41d..81cb01bfc35 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -69,7 +69,8 @@ EXPORT_SYMBOL_GPL(platform_get_irq); * @name: resource name */ struct resource *platform_get_resource_byname(struct platform_device *dev, - unsigned int type, char *name) + unsigned int type, + const char *name) { int i; @@ -88,7 +89,7 @@ EXPORT_SYMBOL_GPL(platform_get_resource_byname); * @dev: platform device * @name: IRQ name */ -int platform_get_irq_byname(struct platform_device *dev, char *name) +int platform_get_irq_byname(struct platform_device *dev, const char *name) { struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name); @@ -244,7 +245,7 @@ int platform_device_add(struct platform_device *pdev) if (pdev->id != -1) dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); else - dev_set_name(&pdev->dev, pdev->name); + dev_set_name(&pdev->dev, "%s", pdev->name); for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource[i]; diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 9742a78c9fe..79a9ae5238a 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -131,6 +131,8 @@ static struct kset *system_kset; int sysdev_class_register(struct sysdev_class *cls) { + int retval; + pr_debug("Registering sysdev class '%s'\n", cls->name); INIT_LIST_HEAD(&cls->drivers); @@ -138,7 +140,11 @@ int sysdev_class_register(struct sysdev_class *cls) cls->kset.kobj.parent = &system_kset->kobj; cls->kset.kobj.ktype = &ktype_sysdev_class; cls->kset.kobj.kset = system_kset; - kobject_set_name(&cls->kset.kobj, cls->name); + + retval = kobject_set_name(&cls->kset.kobj, "%s", cls->name); + if (retval) + return retval; + return kset_register(&cls->kset); } diff --git a/drivers/block/aoe/aoechr.c b/drivers/block/aoe/aoechr.c index 200efc4d2c1..19888354188 100644 --- a/drivers/block/aoe/aoechr.c +++ b/drivers/block/aoe/aoechr.c @@ -266,6 +266,11 @@ static const struct file_operations aoe_fops = { .owner = THIS_MODULE, }; +static char *aoe_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "etherd/%s", dev_name(dev)); +} + int __init aoechr_init(void) { @@ -283,6 +288,8 @@ aoechr_init(void) unregister_chrdev(AOE_MAJOR, "aoechr"); return PTR_ERR(aoe_class); } + aoe_class->nodename = aoe_nodename; + for (i = 0; i < ARRAY_SIZE(chardevs); ++i) device_create(aoe_class, NULL, MKDEV(AOE_MAJOR, chardevs[i].minor), NULL, diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index b22cec97ea1..c7a527c08a0 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -38,7 +38,6 @@ #include <linux/hdreg.h> #include <linux/spinlock.h> #include <linux/compat.h> -#include <linux/blktrace_api.h> #include <asm/uaccess.h> #include <asm/io.h> diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c index 60de5a01e71..f703f547824 100644 --- a/drivers/block/mg_disk.c +++ b/drivers/block/mg_disk.c @@ -22,13 +22,12 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/gpio.h> +#include <linux/mg_disk.h> #define MG_RES_SEC (CONFIG_MG_DISK_RES << 1) /* name for block device */ #define MG_DISK_NAME "mgd" -/* name for platform device */ -#define MG_DEV_NAME "mg_disk" #define MG_DISK_MAJ 0 #define MG_DISK_MAX_PART 16 @@ -103,33 +102,8 @@ #define MG_TMAX_SWRST_TO_RDY 500 #define MG_TMAX_RSTOUT 3000 -/* device attribution */ -/* use mflash as boot device */ -#define MG_BOOT_DEV (1 << 0) -/* use mflash as storage device */ -#define MG_STORAGE_DEV (1 << 1) -/* same as MG_STORAGE_DEV, but bootloader already done reset sequence */ -#define MG_STORAGE_DEV_SKIP_RST (1 << 2) - #define MG_DEV_MASK (MG_BOOT_DEV | MG_STORAGE_DEV | MG_STORAGE_DEV_SKIP_RST) -/* names of GPIO resource */ -#define MG_RST_PIN "mg_rst" -/* except MG_BOOT_DEV, reset-out pin should be assigned */ -#define MG_RSTOUT_PIN "mg_rstout" - -/* private driver data */ -struct mg_drv_data { - /* disk resource */ - u32 use_polling; - - /* device attribution */ - u32 dev_attr; - - /* internally used */ - struct mg_host *host; -}; - /* main structure for mflash driver */ struct mg_host { struct device *dev; diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index d57f1175948..83650e00632 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -430,7 +430,7 @@ static void pkt_sysfs_cleanup(void) /******************************************************************** entries in debugfs - /debugfs/pktcdvd[0-7]/ + /sys/kernel/debug/pktcdvd[0-7]/ info *******************************************************************/ @@ -2855,6 +2855,11 @@ static struct block_device_operations pktcdvd_ops = { .media_changed = pkt_media_changed, }; +static char *pktcdvd_nodename(struct gendisk *gd) +{ + return kasprintf(GFP_KERNEL, "pktcdvd/%s", gd->disk_name); +} + /* * Set up mapping from pktcdvd device to CD-ROM device. */ @@ -2907,6 +2912,7 @@ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev) disk->fops = &pktcdvd_ops; disk->flags = GENHD_FL_REMOVABLE; strcpy(disk->disk_name, pd->name); + disk->nodename = pktcdvd_nodename; disk->private_data = pd; disk->queue = blk_alloc_queue(GFP_KERNEL); if (!disk->queue) @@ -3062,6 +3068,7 @@ static const struct file_operations pkt_ctl_fops = { static struct miscdevice pkt_misc = { .minor = MISC_DYNAMIC_MINOR, .name = DRIVER_NAME, + .name = "pktcdvd/control", .fops = &pkt_ctl_fops }; diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c index aaeeb544228..34cbb7f3efa 100644 --- a/drivers/block/ps3disk.c +++ b/drivers/block/ps3disk.c @@ -120,7 +120,7 @@ static void ps3disk_scatter_gather(struct ps3_storage_device *dev, static int ps3disk_submit_request_sg(struct ps3_storage_device *dev, struct request *req) { - struct ps3disk_private *priv = dev->sbd.core.driver_data; + struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); int write = rq_data_dir(req), res; const char *op = write ? "write" : "read"; u64 start_sector, sectors; @@ -168,7 +168,7 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev, static int ps3disk_submit_flush_request(struct ps3_storage_device *dev, struct request *req) { - struct ps3disk_private *priv = dev->sbd.core.driver_data; + struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); u64 res; dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__); @@ -213,7 +213,7 @@ static void ps3disk_do_request(struct ps3_storage_device *dev, static void ps3disk_request(struct request_queue *q) { struct ps3_storage_device *dev = q->queuedata; - struct ps3disk_private *priv = dev->sbd.core.driver_data; + struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); if (priv->req) { dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__); @@ -245,7 +245,7 @@ static irqreturn_t ps3disk_interrupt(int irq, void *data) return IRQ_HANDLED; } - priv = dev->sbd.core.driver_data; + priv = ps3_system_bus_get_drvdata(&dev->sbd); req = priv->req; if (!req) { dev_dbg(&dev->sbd.core, @@ -364,7 +364,7 @@ static void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs, static int ps3disk_identify(struct ps3_storage_device *dev) { - struct ps3disk_private *priv = dev->sbd.core.driver_data; + struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); struct lv1_ata_cmnd_block ata_cmnd; u16 *id = dev->bounce_buf; u64 res; @@ -445,7 +445,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev) goto fail; } - dev->sbd.core.driver_data = priv; + ps3_system_bus_set_drvdata(_dev, priv); spin_lock_init(&priv->lock); dev->bounce_size = BOUNCE_SIZE; @@ -523,7 +523,7 @@ fail_free_bounce: kfree(dev->bounce_buf); fail_free_priv: kfree(priv); - dev->sbd.core.driver_data = NULL; + ps3_system_bus_set_drvdata(_dev, NULL); fail: mutex_lock(&ps3disk_mask_mutex); __clear_bit(devidx, &ps3disk_mask); @@ -534,7 +534,7 @@ fail: static int ps3disk_remove(struct ps3_system_bus_device *_dev) { struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); - struct ps3disk_private *priv = dev->sbd.core.driver_data; + struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); mutex_lock(&ps3disk_mask_mutex); __clear_bit(MINOR(disk_devt(priv->gendisk)) / PS3DISK_MINORS, @@ -548,7 +548,7 @@ static int ps3disk_remove(struct ps3_system_bus_device *_dev) ps3stor_teardown(dev); kfree(dev->bounce_buf); kfree(priv); - dev->sbd.core.driver_data = NULL; + ps3_system_bus_set_drvdata(_dev, NULL); return 0; } diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c index 8eddef373a9..095f97e6066 100644 --- a/drivers/block/ps3vram.c +++ b/drivers/block/ps3vram.c @@ -14,8 +14,10 @@ #include <linux/seq_file.h> #include <asm/firmware.h> +#include <asm/iommu.h> #include <asm/lv1call.h> #include <asm/ps3.h> +#include <asm/ps3gpu.h> #define DEVICE_NAME "ps3vram" @@ -45,8 +47,6 @@ #define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c #define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104 -#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601 - #define CACHE_PAGE_PRESENT 1 #define CACHE_PAGE_DIRTY 2 @@ -72,8 +72,7 @@ struct ps3vram_priv { u64 memory_handle; u64 context_handle; u32 *ctrl; - u32 *reports; - u8 __iomem *ddr_base; + void *reports; u8 *xdr_buf; u32 *fifo_base; @@ -81,8 +80,8 @@ struct ps3vram_priv { struct ps3vram_cache cache; - /* Used to serialize cache/DMA operations */ - struct mutex lock; + spinlock_t lock; /* protecting list of bios */ + struct bio_list list; }; @@ -103,15 +102,15 @@ static char *size = "256M"; module_param(size, charp, 0); MODULE_PARM_DESC(size, "memory size"); -static u32 *ps3vram_get_notifier(u32 *reports, int notifier) +static u32 *ps3vram_get_notifier(void *reports, int notifier) { - return (void *)reports + DMA_NOTIFIER_OFFSET_BASE + + return reports + DMA_NOTIFIER_OFFSET_BASE + DMA_NOTIFIER_SIZE * notifier; } static void ps3vram_notifier_reset(struct ps3_system_bus_device *dev) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); int i; @@ -122,7 +121,7 @@ static void ps3vram_notifier_reset(struct ps3_system_bus_device *dev) static int ps3vram_notifier_wait(struct ps3_system_bus_device *dev, unsigned int timeout_ms) { - struct ps3vram_priv *priv = dev->core.driver_data; + 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); @@ -137,7 +136,7 @@ static int ps3vram_notifier_wait(struct ps3_system_bus_device *dev, static void ps3vram_init_ring(struct ps3_system_bus_device *dev) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET; priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET; @@ -146,7 +145,7 @@ static void ps3vram_init_ring(struct ps3_system_bus_device *dev) static int ps3vram_wait_ring(struct ps3_system_bus_device *dev, unsigned int timeout_ms) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); do { @@ -175,7 +174,7 @@ static void ps3vram_begin_ring(struct ps3vram_priv *priv, u32 chan, u32 tag, static void ps3vram_rewind_ring(struct ps3_system_bus_device *dev) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); int status; ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET)); @@ -183,20 +182,17 @@ static void ps3vram_rewind_ring(struct ps3_system_bus_device *dev) priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET; /* asking the HV for a blit will kick the FIFO */ - status = lv1_gpu_context_attribute(priv->context_handle, - L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0, - 0, 0, 0); + status = lv1_gpu_fb_blit(priv->context_handle, 0, 0, 0, 0); if (status) - dev_err(&dev->core, - "%s: lv1_gpu_context_attribute failed %d\n", __func__, - status); + dev_err(&dev->core, "%s: lv1_gpu_fb_blit failed %d\n", + __func__, status); priv->fifo_ptr = priv->fifo_base; } static void ps3vram_fire_ring(struct ps3_system_bus_device *dev) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); int status; mutex_lock(&ps3_gpu_mutex); @@ -205,13 +201,10 @@ static void ps3vram_fire_ring(struct ps3_system_bus_device *dev) (priv->fifo_ptr - priv->fifo_base) * sizeof(u32); /* asking the HV for a blit will kick the FIFO */ - status = lv1_gpu_context_attribute(priv->context_handle, - L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0, - 0, 0, 0); + status = lv1_gpu_fb_blit(priv->context_handle, 0, 0, 0, 0); if (status) - dev_err(&dev->core, - "%s: lv1_gpu_context_attribute failed %d\n", __func__, - status); + dev_err(&dev->core, "%s: lv1_gpu_fb_blit failed %d\n", + __func__, status); if ((priv->fifo_ptr - priv->fifo_base) * sizeof(u32) > FIFO_SIZE - 1024) { @@ -225,7 +218,7 @@ static void ps3vram_fire_ring(struct ps3_system_bus_device *dev) static void ps3vram_bind(struct ps3_system_bus_device *dev) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1); ps3vram_out_ring(priv, 0x31337303); @@ -248,7 +241,7 @@ static int ps3vram_upload(struct ps3_system_bus_device *dev, unsigned int src_offset, unsigned int dst_offset, int len, int count) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); ps3vram_begin_ring(priv, UPLOAD_SUBCH, NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); @@ -280,7 +273,7 @@ static int ps3vram_download(struct ps3_system_bus_device *dev, unsigned int src_offset, unsigned int dst_offset, int len, int count) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); @@ -310,7 +303,7 @@ static int ps3vram_download(struct ps3_system_bus_device *dev, static void ps3vram_cache_evict(struct ps3_system_bus_device *dev, int entry) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); struct ps3vram_cache *cache = &priv->cache; if (!(cache->tags[entry].flags & CACHE_PAGE_DIRTY)) @@ -332,7 +325,7 @@ static void ps3vram_cache_evict(struct ps3_system_bus_device *dev, int entry) static void ps3vram_cache_load(struct ps3_system_bus_device *dev, int entry, unsigned int address) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); struct ps3vram_cache *cache = &priv->cache; dev_dbg(&dev->core, "Fetching %d: 0x%08x\n", entry, address); @@ -352,7 +345,7 @@ static void ps3vram_cache_load(struct ps3_system_bus_device *dev, int entry, static void ps3vram_cache_flush(struct ps3_system_bus_device *dev) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); struct ps3vram_cache *cache = &priv->cache; int i; @@ -366,7 +359,7 @@ static void ps3vram_cache_flush(struct ps3_system_bus_device *dev) static unsigned int ps3vram_cache_match(struct ps3_system_bus_device *dev, loff_t address) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); struct ps3vram_cache *cache = &priv->cache; unsigned int base; unsigned int offset; @@ -400,7 +393,7 @@ static unsigned int ps3vram_cache_match(struct ps3_system_bus_device *dev, static int ps3vram_cache_init(struct ps3_system_bus_device *dev) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); priv->cache.page_count = CACHE_PAGE_COUNT; priv->cache.page_size = CACHE_PAGE_SIZE; @@ -419,7 +412,7 @@ static int ps3vram_cache_init(struct ps3_system_bus_device *dev) static void ps3vram_cache_cleanup(struct ps3_system_bus_device *dev) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); ps3vram_cache_flush(dev); kfree(priv->cache.tags); @@ -428,7 +421,7 @@ static void ps3vram_cache_cleanup(struct ps3_system_bus_device *dev) static int ps3vram_read(struct ps3_system_bus_device *dev, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); unsigned int cached, count; dev_dbg(&dev->core, "%s: from=0x%08x len=0x%zx\n", __func__, @@ -449,8 +442,6 @@ static int ps3vram_read(struct ps3_system_bus_device *dev, loff_t from, offset = (unsigned int) (from & (priv->cache.page_size - 1)); avail = priv->cache.page_size - offset; - mutex_lock(&priv->lock); - entry = ps3vram_cache_match(dev, from); cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; @@ -462,8 +453,6 @@ static int ps3vram_read(struct ps3_system_bus_device *dev, loff_t from, avail = count; memcpy(buf, priv->xdr_buf + cached, avail); - mutex_unlock(&priv->lock); - buf += avail; count -= avail; from += avail; @@ -476,7 +465,7 @@ static int ps3vram_read(struct ps3_system_bus_device *dev, loff_t from, static int ps3vram_write(struct ps3_system_bus_device *dev, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); unsigned int cached, count; if (to >= priv->size) @@ -494,8 +483,6 @@ static int ps3vram_write(struct ps3_system_bus_device *dev, loff_t to, offset = (unsigned int) (to & (priv->cache.page_size - 1)); avail = priv->cache.page_size - offset; - mutex_lock(&priv->lock); - entry = ps3vram_cache_match(dev, to); cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; @@ -509,8 +496,6 @@ static int ps3vram_write(struct ps3_system_bus_device *dev, loff_t to, priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY; - mutex_unlock(&priv->lock); - buf += avail; count -= avail; to += avail; @@ -543,28 +528,26 @@ static const struct file_operations ps3vram_proc_fops = { static void __devinit ps3vram_proc_init(struct ps3_system_bus_device *dev) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); struct proc_dir_entry *pde; - pde = proc_create(DEVICE_NAME, 0444, NULL, &ps3vram_proc_fops); - if (!pde) { + pde = proc_create_data(DEVICE_NAME, 0444, NULL, &ps3vram_proc_fops, + priv); + if (!pde) dev_warn(&dev->core, "failed to create /proc entry\n"); - return; - } - pde->data = priv; } -static int ps3vram_make_request(struct request_queue *q, struct bio *bio) +static struct bio *ps3vram_do_bio(struct ps3_system_bus_device *dev, + struct bio *bio) { - struct ps3_system_bus_device *dev = q->queuedata; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); int write = bio_data_dir(bio) == WRITE; const char *op = write ? "write" : "read"; loff_t offset = bio->bi_sector << 9; int error = 0; struct bio_vec *bvec; unsigned int i; - - dev_dbg(&dev->core, "%s\n", __func__); + struct bio *next; bio_for_each_segment(bvec, bio, i) { /* PS3 is ppc64, so we don't handle highmem */ @@ -585,6 +568,7 @@ static int ps3vram_make_request(struct request_queue *q, struct bio *bio) if (retlen != len) { dev_err(&dev->core, "Short %s\n", op); + error = -EIO; goto out; } @@ -594,7 +578,35 @@ static int ps3vram_make_request(struct request_queue *q, struct bio *bio) dev_dbg(&dev->core, "%s completed\n", op); out: + spin_lock_irq(&priv->lock); + bio_list_pop(&priv->list); + next = bio_list_peek(&priv->list); + spin_unlock_irq(&priv->lock); + bio_endio(bio, error); + return next; +} + +static int ps3vram_make_request(struct request_queue *q, struct bio *bio) +{ + struct ps3_system_bus_device *dev = q->queuedata; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); + int busy; + + dev_dbg(&dev->core, "%s\n", __func__); + + spin_lock_irq(&priv->lock); + busy = !bio_list_empty(&priv->list); + bio_list_add(&priv->list, bio); + spin_unlock_irq(&priv->lock); + + if (busy) + return 0; + + do { + bio = ps3vram_do_bio(dev, bio); + } while (bio); + return 0; } @@ -604,8 +616,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) int error, status; struct request_queue *queue; struct gendisk *gendisk; - u64 ddr_lpar, ctrl_lpar, info_lpar, reports_lpar, ddr_size, - reports_size; + u64 ddr_size, ddr_lpar, ctrl_lpar, info_lpar, reports_lpar, + reports_size, xdr_lpar; char *rest; priv = kzalloc(sizeof(*priv), GFP_KERNEL); @@ -614,10 +626,9 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) goto fail; } - mutex_init(&priv->lock); - dev->core.driver_data = priv; - - priv = dev->core.driver_data; + spin_lock_init(&priv->lock); + bio_list_init(&priv->list); + ps3_system_bus_set_drvdata(dev, priv); /* Allocate XDR buffer (1MiB aligned) */ priv->xdr_buf = (void *)__get_free_pages(GFP_KERNEL, @@ -636,7 +647,7 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) if (ps3_open_hv_device(dev)) { dev_err(&dev->core, "ps3_open_hv_device failed\n"); error = -EAGAIN; - goto out_close_gpu; + goto out_free_xdr_buf; } /* Request memory */ @@ -660,7 +671,7 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) dev_err(&dev->core, "lv1_gpu_memory_allocate failed %d\n", status); error = -ENOMEM; - goto out_free_xdr_buf; + goto out_close_gpu; } /* Request context */ @@ -676,9 +687,11 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) } /* Map XDR buffer to RSX */ + xdr_lpar = ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)); status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF, - ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)), - XDR_BUF_SIZE, 0); + xdr_lpar, XDR_BUF_SIZE, + CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | + CBE_IOPTE_M); if (status) { dev_err(&dev->core, "lv1_gpu_context_iomap failed %d\n", status); @@ -686,19 +699,11 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) goto out_free_context; } - priv->ddr_base = ioremap_flags(ddr_lpar, ddr_size, _PAGE_NO_CACHE); - - if (!priv->ddr_base) { - dev_err(&dev->core, "ioremap DDR failed\n"); - error = -ENOMEM; - goto out_free_context; - } - priv->ctrl = ioremap(ctrl_lpar, 64 * 1024); if (!priv->ctrl) { dev_err(&dev->core, "ioremap CTRL failed\n"); error = -ENOMEM; - goto out_unmap_vram; + goto out_unmap_context; } priv->reports = ioremap(reports_lpar, reports_size); @@ -775,8 +780,9 @@ out_unmap_reports: iounmap(priv->reports); out_unmap_ctrl: iounmap(priv->ctrl); -out_unmap_vram: - iounmap(priv->ddr_base); +out_unmap_context: + lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF, xdr_lpar, + XDR_BUF_SIZE, CBE_IOPTE_M); out_free_context: lv1_gpu_context_free(priv->context_handle); out_free_memory: @@ -787,14 +793,14 @@ out_free_xdr_buf: free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE)); fail_free_priv: kfree(priv); - dev->core.driver_data = NULL; + ps3_system_bus_set_drvdata(dev, NULL); fail: return error; } static int ps3vram_remove(struct ps3_system_bus_device *dev) { - struct ps3vram_priv *priv = dev->core.driver_data; + struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); del_gendisk(priv->gendisk); put_disk(priv->gendisk); @@ -803,13 +809,15 @@ static int ps3vram_remove(struct ps3_system_bus_device *dev) ps3vram_cache_cleanup(dev); iounmap(priv->reports); iounmap(priv->ctrl); - iounmap(priv->ddr_base); + lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF, + ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)), + XDR_BUF_SIZE, CBE_IOPTE_M); lv1_gpu_context_free(priv->context_handle); lv1_gpu_memory_free(priv->memory_handle); ps3_close_hv_device(dev); free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE)); kfree(priv); - dev->core.driver_data = NULL; + ps3_system_bus_set_drvdata(dev, NULL); return 0; } diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index c1996829d5e..e53284767f7 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -753,12 +753,12 @@ static int blkfront_probe(struct xenbus_device *dev, /* Front end dir is a number, which is used as the id. */ info->handle = simple_strtoul(strrchr(dev->nodename, '/')+1, NULL, 0); - dev->dev.driver_data = info; + dev_set_drvdata(&dev->dev, info); err = talk_to_backend(dev, info); if (err) { kfree(info); - dev->dev.driver_data = NULL; + dev_set_drvdata(&dev->dev, NULL); return err; } @@ -843,7 +843,7 @@ static int blkif_recover(struct blkfront_info *info) */ static int blkfront_resume(struct xenbus_device *dev) { - struct blkfront_info *info = dev->dev.driver_data; + struct blkfront_info *info = dev_get_drvdata(&dev->dev); int err; dev_dbg(&dev->dev, "blkfront_resume: %s\n", dev->nodename); @@ -922,7 +922,7 @@ static void blkfront_connect(struct blkfront_info *info) */ static void blkfront_closing(struct xenbus_device *dev) { - struct blkfront_info *info = dev->dev.driver_data; + struct blkfront_info *info = dev_get_drvdata(&dev->dev); unsigned long flags; dev_dbg(&dev->dev, "blkfront_closing: %s removed\n", dev->nodename); @@ -957,7 +957,7 @@ static void blkfront_closing(struct xenbus_device *dev) static void backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { - struct blkfront_info *info = dev->dev.driver_data; + struct blkfront_info *info = dev_get_drvdata(&dev->dev); struct block_device *bd; dev_dbg(&dev->dev, "blkfront:backend_changed.\n"); @@ -997,7 +997,7 @@ static void backend_changed(struct xenbus_device *dev, static int blkfront_remove(struct xenbus_device *dev) { - struct blkfront_info *info = dev->dev.driver_data; + struct blkfront_info *info = dev_get_drvdata(&dev->dev); dev_dbg(&dev->dev, "blkfront_remove: %s removed\n", dev->nodename); @@ -1010,7 +1010,7 @@ static int blkfront_remove(struct xenbus_device *dev) static int blkfront_is_ready(struct xenbus_device *dev) { - struct blkfront_info *info = dev->dev.driver_data; + struct blkfront_info *info = dev_get_drvdata(&dev->dev); return info->is_ready; } diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 54481a88776..86105efb4eb 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -4,7 +4,7 @@ * This HVC device driver provides terminal access using * z/VM IUCV communication paths. * - * Copyright IBM Corp. 2008 + * Copyright IBM Corp. 2008, 2009 * * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> */ @@ -15,6 +15,7 @@ #include <asm/ebcdic.h> #include <linux/ctype.h> #include <linux/delay.h> +#include <linux/device.h> #include <linux/init.h> #include <linux/mempool.h> #include <linux/moduleparam.h> @@ -74,6 +75,7 @@ struct hvc_iucv_private { wait_queue_head_t sndbuf_waitq; /* wait for send completion */ struct list_head tty_outqueue; /* outgoing IUCV messages */ struct list_head tty_inqueue; /* incoming IUCV messages */ + struct device *dev; /* device structure */ }; struct iucv_tty_buffer { @@ -542,7 +544,68 @@ static void flush_sndbuf_sync(struct hvc_iucv_private *priv) if (sync_wait) wait_event_timeout(priv->sndbuf_waitq, - tty_outqueue_empty(priv), HZ); + tty_outqueue_empty(priv), HZ/10); +} + +/** + * hvc_iucv_hangup() - Sever IUCV path and schedule hvc tty hang up + * @priv: Pointer to hvc_iucv_private structure + * + * This routine severs an existing IUCV communication path and hangs + * up the underlying HVC terminal device. + * The hang-up occurs only if an IUCV communication path is established; + * otherwise there is no need to hang up the terminal device. + * + * The IUCV HVC hang-up is separated into two steps: + * 1. After the IUCV path has been severed, the iucv_state is set to + * IUCV_SEVERED. + * 2. Later, when the HVC thread calls hvc_iucv_get_chars(), the + * IUCV_SEVERED state causes the tty hang-up in the HVC layer. + * + * If the tty has not yet been opened, clean up the hvc_iucv_private + * structure to allow re-connects. + * If the tty has been opened, let get_chars() return -EPIPE to signal + * the HVC layer to hang up the tty and, if so, wake up the HVC thread + * to call get_chars()... + * + * Special notes on hanging up a HVC terminal instantiated as console: + * Hang-up: 1. do_tty_hangup() replaces file ops (= hung_up_tty_fops) + * 2. do_tty_hangup() calls tty->ops->close() for console_filp + * => no hangup notifier is called by HVC (default) + * 2. hvc_close() returns because of tty_hung_up_p(filp) + * => no delete notifier is called! + * Finally, the back-end is not being notified, thus, the tty session is + * kept active (TTY_OPEN) to be ready for re-connects. + * + * Locking: spin_lock(&priv->lock) w/o disabling bh + */ +static void hvc_iucv_hangup(struct hvc_iucv_private *priv) +{ + struct iucv_path *path; + + path = NULL; + spin_lock(&priv->lock); + if (priv->iucv_state == IUCV_CONNECTED) { + path = priv->path; + priv->path = NULL; + priv->iucv_state = IUCV_SEVERED; + if (priv->tty_state == TTY_CLOSED) + hvc_iucv_cleanup(priv); + else + /* console is special (see above) */ + if (priv->is_console) { + hvc_iucv_cleanup(priv); + priv->tty_state = TTY_OPENED; + } else + hvc_kick(); + } + spin_unlock(&priv->lock); + + /* finally sever path (outside of priv->lock due to lock ordering) */ + if (path) { + iucv_path_sever(path, NULL); + iucv_path_free(path); + } } /** @@ -735,11 +798,8 @@ out_path_handled: * @ipuser: User specified data for this path * (AF_IUCV: port/service name and originator port) * - * The function also severs the path (as required by the IUCV protocol) and - * sets the iucv state to IUCV_SEVERED for the associated struct - * hvc_iucv_private instance. Later, the IUCV_SEVERED state triggers a tty - * hangup (hvc_iucv_get_chars() / hvc_iucv_write()). - * If tty portion of the HVC is closed, clean up the outqueue. + * This function calls the hvc_iucv_hangup() function for the + * respective IUCV HVC terminal. * * Locking: struct hvc_iucv_private->lock */ @@ -747,33 +807,7 @@ static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) { struct hvc_iucv_private *priv = path->private; - spin_lock(&priv->lock); - priv->iucv_state = IUCV_SEVERED; - - /* If the tty has not yet been opened, clean up the hvc_iucv_private - * structure to allow re-connects. - * This is also done for our console device because console hangups - * are handled specially and no notifier is called by HVC. - * The tty session is active (TTY_OPEN) and ready for re-connects... - * - * If it has been opened, let get_chars() return -EPIPE to signal the - * HVC layer to hang up the tty. - * If so, we need to wake up the HVC thread to call get_chars()... - */ - priv->path = NULL; - if (priv->tty_state == TTY_CLOSED) - hvc_iucv_cleanup(priv); - else - if (priv->is_console) { - hvc_iucv_cleanup(priv); - priv->tty_state = TTY_OPENED; - } else - hvc_kick(); - spin_unlock(&priv->lock); - - /* finally sever path (outside of priv->lock due to lock ordering) */ - iucv_path_sever(path, ipuser); - iucv_path_free(path); + hvc_iucv_hangup(priv); } /** @@ -853,6 +887,37 @@ static void hvc_iucv_msg_complete(struct iucv_path *path, destroy_tty_buffer_list(&list_remove); } +/** + * hvc_iucv_pm_freeze() - Freeze PM callback + * @dev: IUVC HVC terminal device + * + * Sever an established IUCV communication path and + * trigger a hang-up of the underlying HVC terminal. + */ +static int hvc_iucv_pm_freeze(struct device *dev) +{ + struct hvc_iucv_private *priv = dev_get_drvdata(dev); + + local_bh_disable(); + hvc_iucv_hangup(priv); + local_bh_enable(); + + return 0; +} + +/** + * hvc_iucv_pm_restore_thaw() - Thaw and restore PM callback + * @dev: IUVC HVC terminal device + * + * Wake up the HVC thread to trigger hang-up and respective + * HVC back-end notifier invocations. + */ +static int hvc_iucv_pm_restore_thaw(struct device *dev) +{ + hvc_kick(); + return 0; +} + /* HVC operations */ static struct hv_ops hvc_iucv_ops = { @@ -863,6 +928,20 @@ static struct hv_ops hvc_iucv_ops = { .notifier_hangup = hvc_iucv_notifier_hangup, }; +/* Suspend / resume device operations */ +static struct dev_pm_ops hvc_iucv_pm_ops = { + .freeze = hvc_iucv_pm_freeze, + .thaw = hvc_iucv_pm_restore_thaw, + .restore = hvc_iucv_pm_restore_thaw, +}; + +/* IUCV HVC device driver */ +static struct device_driver hvc_iucv_driver = { + .name = KMSG_COMPONENT, + .bus = &iucv_bus, + .pm = &hvc_iucv_pm_ops, +}; + /** * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance * @id: hvc_iucv_table index @@ -897,14 +976,12 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) /* set console flag */ priv->is_console = is_console; - /* finally allocate hvc */ + /* allocate hvc device */ priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, /* PAGE_SIZE */ HVC_IUCV_MAGIC + id, &hvc_iucv_ops, 256); if (IS_ERR(priv->hvc)) { rc = PTR_ERR(priv->hvc); - free_page((unsigned long) priv->sndbuf); - kfree(priv); - return rc; + goto out_error_hvc; } /* notify HVC thread instead of using polling */ @@ -915,8 +992,45 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) memcpy(priv->srv_name, name, 8); ASCEBC(priv->srv_name, 8); + /* create and setup device */ + priv->dev = kzalloc(sizeof(*priv->dev), GFP_KERNEL); + if (!priv->dev) { + rc = -ENOMEM; + goto out_error_dev; + } + dev_set_name(priv->dev, "hvc_iucv%d", id); + dev_set_drvdata(priv->dev, priv); + priv->dev->bus = &iucv_bus; + priv->dev->parent = iucv_root; + priv->dev->driver = &hvc_iucv_driver; + priv->dev->release = (void (*)(struct device *)) kfree; + rc = device_register(priv->dev); + if (rc) { + kfree(priv->dev); + goto out_error_dev; + } + hvc_iucv_table[id] = priv; return 0; + +out_error_dev: + hvc_remove(priv->hvc); +out_error_hvc: + free_page((unsigned long) priv->sndbuf); + kfree(priv); + + return rc; +} + +/** + * hvc_iucv_destroy() - Destroy and free hvc_iucv_private instances + */ +static void __init hvc_iucv_destroy(struct hvc_iucv_private *priv) +{ + hvc_remove(priv->hvc); + device_unregister(priv->dev); + free_page((unsigned long) priv->sndbuf); + kfree(priv); } /** @@ -1109,6 +1223,11 @@ static int __init hvc_iucv_init(void) goto out_error; } + /* register IUCV HVC device driver */ + rc = driver_register(&hvc_iucv_driver); + if (rc) + goto out_error; + /* parse hvc_iucv_allow string and create z/VM user ID filter list */ if (hvc_iucv_filter_string) { rc = hvc_iucv_setup_filter(hvc_iucv_filter_string); @@ -1183,15 +1302,14 @@ out_error_iucv: iucv_unregister(&hvc_iucv_handler, 0); out_error_hvc: for (i = 0; i < hvc_iucv_devices; i++) - if (hvc_iucv_table[i]) { - if (hvc_iucv_table[i]->hvc) - hvc_remove(hvc_iucv_table[i]->hvc); - kfree(hvc_iucv_table[i]); - } + if (hvc_iucv_table[i]) + hvc_iucv_destroy(hvc_iucv_table[i]); out_error_memory: mempool_destroy(hvc_iucv_mempool); kmem_cache_destroy(hvc_iucv_buffer_cache); out_error: + if (hvc_iucv_filter) + kfree(hvc_iucv_filter); hvc_iucv_devices = 0; /* ensure that we do not provide any device */ return rc; } diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c index c76bccf5354..7d64e4230e6 100644 --- a/drivers/char/hvcs.c +++ b/drivers/char/hvcs.c @@ -347,7 +347,7 @@ static void __exit hvcs_module_exit(void); static inline struct hvcs_struct *from_vio_dev(struct vio_dev *viod) { - return viod->dev.driver_data; + return dev_get_drvdata(&viod->dev); } /* The sysfs interface for the driver and devices */ @@ -785,7 +785,7 @@ static int __devinit hvcs_probe( kref_init(&hvcsd->kref); hvcsd->vdev = dev; - dev->dev.driver_data = hvcsd; + dev_set_drvdata(&dev->dev, hvcsd); hvcsd->index = index; @@ -831,7 +831,7 @@ static int __devinit hvcs_probe( static int __devexit hvcs_remove(struct vio_dev *dev) { - struct hvcs_struct *hvcsd = dev->dev.driver_data; + struct hvcs_struct *hvcsd = dev_get_drvdata(&dev->dev); unsigned long flags; struct tty_struct *tty; diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index e5d583c84e4..fc93e2fc7c7 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -153,6 +153,7 @@ static const struct file_operations rng_chrdev_ops = { static struct miscdevice rng_miscdev = { .minor = RNG_MISCDEV_MINOR, .name = RNG_MODULE_NAME, + .devnode = "hwrng", .fops = &rng_chrdev_ops, }; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 259644646b8..d2e698096ac 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2375,14 +2375,14 @@ static int __devinit ipmi_of_probe(struct of_device *dev, info->io.addr_data, info->io.regsize, info->io.regspacing, info->irq); - dev->dev.driver_data = (void *) info; + dev_set_drvdata(&dev->dev, info); return try_smi_init(info); } static int __devexit ipmi_of_remove(struct of_device *dev) { - cleanup_one_si(dev->dev.driver_data); + cleanup_one_si(dev_get_drvdata(&dev->dev)); return 0; } diff --git a/drivers/char/misc.c b/drivers/char/misc.c index a5e0db9d766..62c99fa59e2 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -168,7 +168,6 @@ static const struct file_operations misc_fops = { .open = misc_open, }; - /** * misc_register - register a miscellaneous device * @misc: device structure @@ -217,8 +216,8 @@ int misc_register(struct miscdevice * misc) misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7); dev = MKDEV(MISC_MAJOR, misc->minor); - misc->this_device = device_create(misc_class, misc->parent, dev, NULL, - "%s", misc->name); + misc->this_device = device_create(misc_class, misc->parent, dev, + misc, "%s", misc->name); if (IS_ERR(misc->this_device)) { err = PTR_ERR(misc->this_device); goto out; @@ -264,6 +263,15 @@ int misc_deregister(struct miscdevice *misc) EXPORT_SYMBOL(misc_register); EXPORT_SYMBOL(misc_deregister); +static char *misc_nodename(struct device *dev) +{ + struct miscdevice *c = dev_get_drvdata(dev); + + if (c->devnode) + return kstrdup(c->devnode, GFP_KERNEL); + return NULL; +} + static int __init misc_init(void) { int err; @@ -279,6 +287,7 @@ static int __init misc_init(void) err = -EIO; if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) goto fail_printk; + misc_class->nodename = misc_nodename; return 0; fail_printk: diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index afbe45676d7..f424d394a28 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c @@ -33,48 +33,64 @@ struct ps3flash_private { struct mutex mutex; /* Bounce buffer mutex */ + u64 chunk_sectors; + int tag; /* Start sector of buffer, -1 if invalid */ + bool dirty; }; static struct ps3_storage_device *ps3flash_dev; -static ssize_t ps3flash_read_write_sectors(struct ps3_storage_device *dev, - u64 lpar, u64 start_sector, - u64 sectors, int write) +static int ps3flash_read_write_sectors(struct ps3_storage_device *dev, + u64 start_sector, int write) { - u64 res = ps3stor_read_write_sectors(dev, lpar, start_sector, sectors, + struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); + u64 res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, + start_sector, priv->chunk_sectors, write); if (res) { dev_err(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__, __LINE__, write ? "write" : "read", res); return -EIO; } - return sectors; + return 0; } -static ssize_t ps3flash_read_sectors(struct ps3_storage_device *dev, - u64 start_sector, u64 sectors, - unsigned int sector_offset) +static int ps3flash_writeback(struct ps3_storage_device *dev) { - u64 max_sectors, lpar; + struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); + int res; - max_sectors = dev->bounce_size / dev->blk_size; - if (sectors > max_sectors) { - dev_dbg(&dev->sbd.core, "%s:%u Limiting sectors to %llu\n", - __func__, __LINE__, max_sectors); - sectors = max_sectors; - } + if (!priv->dirty || priv->tag < 0) + return 0; - lpar = dev->bounce_lpar + sector_offset * dev->blk_size; - return ps3flash_read_write_sectors(dev, lpar, start_sector, sectors, - 0); + res = ps3flash_read_write_sectors(dev, priv->tag, 1); + if (res) + return res; + + priv->dirty = false; + return 0; } -static ssize_t ps3flash_write_chunk(struct ps3_storage_device *dev, - u64 start_sector) +static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector) { - u64 sectors = dev->bounce_size / dev->blk_size; - return ps3flash_read_write_sectors(dev, dev->bounce_lpar, start_sector, - sectors, 1); + struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); + int res; + + if (start_sector == priv->tag) + return 0; + + res = ps3flash_writeback(dev); + if (res) + return res; + + priv->tag = -1; + + res = ps3flash_read_write_sectors(dev, start_sector, 0); + if (res) + return res; + + priv->tag = start_sector; + return 0; } static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin) @@ -104,18 +120,19 @@ out: return res; } -static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count, - loff_t *pos) +static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf, + size_t count, loff_t *pos) { struct ps3_storage_device *dev = ps3flash_dev; - struct ps3flash_private *priv = dev->sbd.core.driver_data; - u64 size, start_sector, end_sector, offset; - ssize_t sectors_read; + struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); + u64 size, sector, offset; + int res; size_t remaining, n; + const void *src; dev_dbg(&dev->sbd.core, - "%s:%u: Reading %zu bytes at position %lld to user 0x%p\n", - __func__, __LINE__, count, *pos, buf); + "%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n", + __func__, __LINE__, count, *pos, userbuf, kernelbuf); size = dev->regions[dev->region_idx].size*dev->blk_size; if (*pos >= size || !count) @@ -128,61 +145,63 @@ static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count, count = size - *pos; } - start_sector = *pos / dev->blk_size; - offset = *pos % dev->blk_size; - end_sector = DIV_ROUND_UP(*pos + count, dev->blk_size); + sector = *pos / dev->bounce_size * priv->chunk_sectors; + offset = *pos % dev->bounce_size; remaining = count; do { + n = min_t(u64, remaining, dev->bounce_size - offset); + src = dev->bounce_buf + offset; + mutex_lock(&priv->mutex); - sectors_read = ps3flash_read_sectors(dev, start_sector, - end_sector-start_sector, - 0); - if (sectors_read < 0) { - mutex_unlock(&priv->mutex); + res = ps3flash_fetch(dev, sector); + if (res) goto fail; - } - n = min_t(u64, remaining, sectors_read*dev->blk_size-offset); dev_dbg(&dev->sbd.core, - "%s:%u: copy %lu bytes from 0x%p to user 0x%p\n", - __func__, __LINE__, n, dev->bounce_buf+offset, buf); - if (copy_to_user(buf, dev->bounce_buf+offset, n)) { - mutex_unlock(&priv->mutex); - sectors_read = -EFAULT; - goto fail; + "%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n", + __func__, __LINE__, n, src, userbuf, kernelbuf); + if (userbuf) { + if (copy_to_user(userbuf, src, n)) { + res = -EFAULT; + goto fail; + } + userbuf += n; + } + if (kernelbuf) { + memcpy(kernelbuf, src, n); + kernelbuf += n; } mutex_unlock(&priv->mutex); *pos += n; - buf += n; remaining -= n; - start_sector += sectors_read; + sector += priv->chunk_sectors; offset = 0; } while (remaining > 0); return count; fail: - return sectors_read; + mutex_unlock(&priv->mutex); + return res; } -static ssize_t ps3flash_write(struct file *file, const char __user *buf, - size_t count, loff_t *pos) +static ssize_t ps3flash_write(const char __user *userbuf, + const void *kernelbuf, size_t count, loff_t *pos) { struct ps3_storage_device *dev = ps3flash_dev; - struct ps3flash_private *priv = dev->sbd.core.driver_data; - u64 size, chunk_sectors, start_write_sector, end_write_sector, - end_read_sector, start_read_sector, head, tail, offset; - ssize_t res; + struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); + u64 size, sector, offset; + int res = 0; size_t remaining, n; - unsigned int sec_off; + void *dst; dev_dbg(&dev->sbd.core, - "%s:%u: Writing %zu bytes at position %lld from user 0x%p\n", - __func__, __LINE__, count, *pos, buf); + "%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n", + __func__, __LINE__, count, *pos, userbuf, kernelbuf); size = dev->regions[dev->region_idx].size*dev->blk_size; if (*pos >= size || !count) @@ -195,89 +214,46 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf, count = size - *pos; } - chunk_sectors = dev->bounce_size / dev->blk_size; - - start_write_sector = *pos / dev->bounce_size * chunk_sectors; + sector = *pos / dev->bounce_size * priv->chunk_sectors; offset = *pos % dev->bounce_size; - end_write_sector = DIV_ROUND_UP(*pos + count, dev->bounce_size) * - chunk_sectors; - - end_read_sector = DIV_ROUND_UP(*pos, dev->blk_size); - start_read_sector = (*pos + count) / dev->blk_size; - - /* - * As we have to write in 256 KiB chunks, while we can read in blk_size - * (usually 512 bytes) chunks, we perform the following steps: - * 1. Read from start_write_sector to end_read_sector ("head") - * 2. Read from start_read_sector to end_write_sector ("tail") - * 3. Copy data to buffer - * 4. Write from start_write_sector to end_write_sector - * All of this is complicated by using only one 256 KiB bounce buffer. - */ - - head = end_read_sector - start_write_sector; - tail = end_write_sector - start_read_sector; remaining = count; do { + n = min_t(u64, remaining, dev->bounce_size - offset); + dst = dev->bounce_buf + offset; + mutex_lock(&priv->mutex); - if (end_read_sector >= start_read_sector) { - /* Merge head and tail */ - dev_dbg(&dev->sbd.core, - "Merged head and tail: %llu sectors at %llu\n", - chunk_sectors, start_write_sector); - res = ps3flash_read_sectors(dev, start_write_sector, - chunk_sectors, 0); - if (res < 0) + if (n != dev->bounce_size) + res = ps3flash_fetch(dev, sector); + else if (sector != priv->tag) + res = ps3flash_writeback(dev); + if (res) + goto fail; + + dev_dbg(&dev->sbd.core, + "%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n", + __func__, __LINE__, n, userbuf, kernelbuf, dst); + if (userbuf) { + if (copy_from_user(dst, userbuf, n)) { + res = -EFAULT; goto fail; - } else { - if (head) { - /* Read head */ - dev_dbg(&dev->sbd.core, - "head: %llu sectors at %llu\n", head, - start_write_sector); - res = ps3flash_read_sectors(dev, - start_write_sector, - head, 0); - if (res < 0) - goto fail; - } - if (start_read_sector < - start_write_sector+chunk_sectors) { - /* Read tail */ - dev_dbg(&dev->sbd.core, - "tail: %llu sectors at %llu\n", tail, - start_read_sector); - sec_off = start_read_sector-start_write_sector; - res = ps3flash_read_sectors(dev, - start_read_sector, - tail, sec_off); - if (res < 0) - goto fail; } + userbuf += n; } - - n = min_t(u64, remaining, dev->bounce_size-offset); - dev_dbg(&dev->sbd.core, - "%s:%u: copy %lu bytes from user 0x%p to 0x%p\n", - __func__, __LINE__, n, buf, dev->bounce_buf+offset); - if (copy_from_user(dev->bounce_buf+offset, buf, n)) { - res = -EFAULT; - goto fail; + if (kernelbuf) { + memcpy(dst, kernelbuf, n); + kernelbuf += n; } - res = ps3flash_write_chunk(dev, start_write_sector); - if (res < 0) - goto fail; + priv->tag = sector; + priv->dirty = true; mutex_unlock(&priv->mutex); *pos += n; - buf += n; remaining -= n; - start_write_sector += chunk_sectors; - head = 0; + sector += priv->chunk_sectors; offset = 0; } while (remaining > 0); @@ -288,6 +264,51 @@ fail: return res; } +static ssize_t ps3flash_user_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + return ps3flash_read(buf, NULL, count, pos); +} + +static ssize_t ps3flash_user_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + return ps3flash_write(buf, NULL, count, pos); +} + +static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos) +{ + return ps3flash_read(NULL, buf, count, &pos); +} + +static ssize_t ps3flash_kernel_write(const void *buf, size_t count, + loff_t pos) +{ + ssize_t res; + int wb; + + res = ps3flash_write(NULL, buf, count, &pos); + if (res < 0) + return res; + + /* Make kernel writes synchronous */ + wb = ps3flash_writeback(ps3flash_dev); + if (wb) + return wb; + + return res; +} + +static int ps3flash_flush(struct file *file, fl_owner_t id) +{ + return ps3flash_writeback(ps3flash_dev); +} + +static int ps3flash_fsync(struct file *file, struct dentry *dentry, + int datasync) +{ + return ps3flash_writeback(ps3flash_dev); +} static irqreturn_t ps3flash_interrupt(int irq, void *data) { @@ -312,12 +333,18 @@ static irqreturn_t ps3flash_interrupt(int irq, void *data) return IRQ_HANDLED; } - static const struct file_operations ps3flash_fops = { .owner = THIS_MODULE, .llseek = ps3flash_llseek, - .read = ps3flash_read, - .write = ps3flash_write, + .read = ps3flash_user_read, + .write = ps3flash_user_write, + .flush = ps3flash_flush, + .fsync = ps3flash_fsync, +}; + +static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = { + .read = ps3flash_kernel_read, + .write = ps3flash_kernel_write, }; static struct miscdevice ps3flash_misc = { @@ -366,11 +393,13 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev) goto fail; } - dev->sbd.core.driver_data = priv; + ps3_system_bus_set_drvdata(&dev->sbd, priv); mutex_init(&priv->mutex); + priv->tag = -1; dev->bounce_size = ps3flash_bounce_buffer.size; dev->bounce_buf = ps3flash_bounce_buffer.address; + priv->chunk_sectors = dev->bounce_size / dev->blk_size; error = ps3stor_setup(dev, ps3flash_interrupt); if (error) @@ -386,13 +415,15 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev) dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n", __func__, __LINE__, ps3flash_misc.minor); + + ps3_os_area_flash_register(&ps3flash_kernel_ops); return 0; fail_teardown: ps3stor_teardown(dev); fail_free_priv: kfree(priv); - dev->sbd.core.driver_data = NULL; + ps3_system_bus_set_drvdata(&dev->sbd, NULL); fail: ps3flash_dev = NULL; return error; @@ -402,10 +433,11 @@ static int ps3flash_remove(struct ps3_system_bus_device *_dev) { struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); + ps3_os_area_flash_register(NULL); misc_deregister(&ps3flash_misc); ps3stor_teardown(dev); - kfree(dev->sbd.core.driver_data); - dev->sbd.core.driver_data = NULL; + kfree(ps3_system_bus_get_drvdata(&dev->sbd)); + ps3_system_bus_set_drvdata(&dev->sbd, NULL); ps3flash_dev = NULL; return 0; } diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 5acd29e6e04..daebe1ba43d 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -95,23 +95,34 @@ static void pty_unthrottle(struct tty_struct *tty) * a count. * * FIXME: Our pty_write method is called with our ldisc lock held but - * not our partners. We can't just take the other one blindly without - * risking deadlocks. + * not our partners. We can't just wait on the other one blindly without + * risking deadlocks. At some point when everything has settled down we need + * to look into making pty_write at least able to sleep over an ldisc change. + * + * The return on no ldisc is a bit counter intuitive but the logic works + * like this. During an ldisc change the other end will flush its buffers. We + * thus return the full length which is identical to the case where we had + * proper locking and happened to queue the bytes just before the flush during + * the ldisc change. */ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct tty_struct *to = tty->link; - int c; + struct tty_ldisc *ld; + int c = count; if (!to || tty->stopped) return 0; - - c = to->receive_room; - if (c > count) - c = count; - to->ldisc->ops->receive_buf(to, buf, NULL, c); - + ld = tty_ldisc_ref(to); + + if (ld) { + c = to->receive_room; + if (c > count) + c = count; + ld->ops->receive_buf(to, buf, NULL, c); + tty_ldisc_deref(ld); + } return c; } @@ -145,14 +156,23 @@ static int pty_write_room(struct tty_struct *tty) static int pty_chars_in_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; - int count; + struct tty_ldisc *ld; + int count = 0; /* We should get the line discipline lock for "tty->link" */ - if (!to || !to->ldisc->ops->chars_in_buffer) + if (!to) + return 0; + /* We cannot take a sleeping reference here without deadlocking with + an ldisc change - but it doesn't really matter */ + ld = tty_ldisc_ref(to); + if (ld == NULL) return 0; /* The ldisc must report 0 if no characters available to be read */ - count = to->ldisc->ops->chars_in_buffer(to); + if (ld->ops->chars_in_buffer) + count = ld->ops->chars_in_buffer(to); + + tty_ldisc_deref(ld); if (tty->driver->subtype == PTY_TYPE_SLAVE) return count; @@ -182,12 +202,19 @@ static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; unsigned long flags; + struct tty_ldisc *ld; if (!to) return; + ld = tty_ldisc_ref(to); + + /* The other end is changing discipline */ + if (!ld) + return; - if (to->ldisc->ops->flush_buffer) + if (ld->ops->flush_buffer) to->ldisc->ops->flush_buffer(to); + tty_ldisc_deref(ld); if (to->packet) { spin_lock_irqsave(&tty->ctrl_lock, flags); diff --git a/drivers/char/raw.c b/drivers/char/raw.c index db32f0e4c7d..05f9d18b936 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -261,6 +261,11 @@ static const struct file_operations raw_ctl_fops = { static struct cdev raw_cdev; +static char *raw_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "raw/%s", dev_name(dev)); +} + static int __init raw_init(void) { dev_t dev = MKDEV(RAW_MAJOR, 0); @@ -284,6 +289,7 @@ static int __init raw_init(void) ret = PTR_ERR(raw_class); goto error_region; } + raw_class->nodename = raw_nodename; device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl"); return 0; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 939e198d767..a3afa0c387c 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1263,7 +1263,9 @@ static int tty_reopen(struct tty_struct *tty) tty->count++; tty->driver = driver; /* N.B. why do this every time?? */ + mutex_lock(&tty->ldisc_mutex); WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); + mutex_unlock(&tty->ldisc_mutex); return 0; } diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 8116bb1c8f8..b24f6c6a1ea 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -947,7 +947,6 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, void __user *p = (void __user *)arg; int ret = 0; struct ktermios kterm; - struct termiox ktermx; if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) @@ -1049,7 +1048,8 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, return ret; #endif #ifdef TCGETX - case TCGETX: + case TCGETX: { + struct termiox ktermx; if (real_tty->termiox == NULL) return -EINVAL; mutex_lock(&real_tty->termios_mutex); @@ -1058,6 +1058,7 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, if (copy_to_user(p, &ktermx, sizeof(struct termiox))) ret = -EFAULT; return ret; + } case TCSETX: return set_termiox(real_tty, p, 0); case TCSETXW: diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index 39c8f86dedd..a19e935847b 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c @@ -148,8 +148,10 @@ static struct tty_ldisc *tty_ldisc_try_get(int disc) } } spin_unlock_irqrestore(&tty_ldisc_lock, flags); - if (err) + if (err) { + kfree(ld); return ERR_PTR(err); + } return ld; } @@ -205,6 +207,7 @@ static void tty_ldisc_put(struct tty_ldisc *ld) ldo->refcount--; module_put(ldo->owner); spin_unlock_irqrestore(&tty_ldisc_lock, flags); + WARN_ON(ld->refcount); kfree(ld); } @@ -262,7 +265,7 @@ const struct file_operations tty_ldiscs_proc_fops = { * @ld: line discipline * * Install an instance of a line discipline into a tty structure. The - * ldisc must have a reference count above zero to ensure it remains/ + * ldisc must have a reference count above zero to ensure it remains. * The tty instance refcount starts at zero. * * Locking: @@ -791,6 +794,8 @@ void tty_ldisc_hangup(struct tty_struct *tty) /* Avoid racing set_ldisc */ mutex_lock(&tty->ldisc_mutex); /* Switch back to N_TTY */ + tty_ldisc_halt(tty); + tty_ldisc_wait_idle(tty); tty_ldisc_reinit(tty); /* At this point we have a closed ldisc and we want to reopen it. We could defer this to the next open but diff --git a/drivers/eisa/pci_eisa.c b/drivers/eisa/pci_eisa.c index 74edb1d0110..0dd0f633b18 100644 --- a/drivers/eisa/pci_eisa.c +++ b/drivers/eisa/pci_eisa.c @@ -31,11 +31,11 @@ static int __init pci_eisa_init(struct pci_dev *pdev, } pci_eisa_root.dev = &pdev->dev; - pci_eisa_root.dev->driver_data = &pci_eisa_root; pci_eisa_root.res = pdev->bus->resource[0]; pci_eisa_root.bus_base_addr = pdev->bus->resource[0]->start; pci_eisa_root.slots = EISA_MAX_SLOTS; pci_eisa_root.dma_mask = pdev->dma_mask; + dev_set_drvdata(pci_eisa_root.dev, &pci_eisa_root); if (eisa_root_register (&pci_eisa_root)) { printk (KERN_ERR "pci_eisa : Could not register EISA root\n"); diff --git a/drivers/eisa/virtual_root.c b/drivers/eisa/virtual_root.c index 3074879f231..535e4f9c83f 100644 --- a/drivers/eisa/virtual_root.c +++ b/drivers/eisa/virtual_root.c @@ -57,7 +57,7 @@ static int __init virtual_eisa_root_init (void) eisa_bus_root.force_probe = force_probe; - eisa_root_dev.dev.driver_data = &eisa_bus_root; + dev_set_drvdata(&eisa_root_dev.dev, &eisa_bus_root); if (eisa_root_register (&eisa_bus_root)) { /* A real bridge may have been registered before diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 2bcf51557c7..a70e66e78c7 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -1125,7 +1125,7 @@ static int sbp2_probe(struct device *dev) return -ENOMEM; tgt = (struct sbp2_target *)shost->hostdata; - unit->device.driver_data = tgt; + dev_set_drvdata(&unit->device, tgt); tgt->unit = unit; kref_init(&tgt->kref); INIT_LIST_HEAD(&tgt->lu_list); @@ -1180,7 +1180,7 @@ static int sbp2_probe(struct device *dev) static int sbp2_remove(struct device *dev) { struct fw_unit *unit = fw_unit(dev); - struct sbp2_target *tgt = unit->device.driver_data; + struct sbp2_target *tgt = dev_get_drvdata(&unit->device); sbp2_target_put(tgt); return 0; @@ -1240,7 +1240,7 @@ static void sbp2_reconnect(struct work_struct *work) static void sbp2_update(struct fw_unit *unit) { - struct sbp2_target *tgt = unit->device.driver_data; + struct sbp2_target *tgt = dev_get_drvdata(&unit->device); struct sbp2_logical_unit *lu; fw_device_enable_phys_dma(fw_device(unit->device.parent)); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index c77c6c6d9d2..6ce0e2667a8 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -105,7 +105,7 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count, ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO, root, tmp, &drm_debugfs_fops); if (!ent) { - DRM_ERROR("Cannot create /debugfs/dri/%s/%s\n", + DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/%s\n", name, files[i].name); drm_free(tmp, sizeof(struct drm_info_node), _DRM_DRIVER); @@ -133,9 +133,9 @@ EXPORT_SYMBOL(drm_debugfs_create_files); * \param minor device minor number * \param root DRI debugfs dir entry. * - * Create the DRI debugfs root entry "/debugfs/dri", the device debugfs root entry - * "/debugfs/dri/%minor%/", and each entry in debugfs_list as - * "/debugfs/dri/%minor%/%name%". + * Create the DRI debugfs root entry "/sys/kernel/debug/dri", the device debugfs root entry + * "/sys/kernel/debug/dri/%minor%/", and each entry in debugfs_list as + * "/sys/kernel/debug/dri/%minor%/%name%". */ int drm_debugfs_init(struct drm_minor *minor, int minor_id, struct dentry *root) @@ -148,7 +148,7 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id, sprintf(name, "%d", minor_id); minor->debugfs_root = debugfs_create_dir(name, root); if (!minor->debugfs_root) { - DRM_ERROR("Cannot create /debugfs/dri/%s\n", name); + DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s\n", name); return -1; } @@ -165,7 +165,7 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id, ret = dev->driver->debugfs_init(minor); if (ret) { DRM_ERROR("DRM: Driver failed to initialize " - "/debugfs/dri.\n"); + "/sys/kernel/debug/dri.\n"); return ret; } } diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 019b7c57823..1bf7efd8d33 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -339,7 +339,7 @@ static int __init drm_core_init(void) drm_debugfs_root = debugfs_create_dir("dri", NULL); if (!drm_debugfs_root) { - DRM_ERROR("Cannot create /debugfs/dri\n"); + DRM_ERROR("Cannot create /sys/kernel/debug/dri\n"); ret = -1; goto err_p3; } diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 89050684fe0..387a8de1bc7 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -343,7 +343,7 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t #if defined(CONFIG_DEBUG_FS) ret = drm_debugfs_init(new_minor, minor_id, drm_debugfs_root); if (ret) { - DRM_ERROR("DRM: Failed to initialize /debugfs/dri.\n"); + DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n"); goto err_g2; } #endif diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 9987ab88083..85ec31b3ff0 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -70,6 +70,11 @@ static ssize_t version_show(struct class *dev, char *buf) CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); } +static char *drm_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); +} + static CLASS_ATTR(version, S_IRUGO, version_show, NULL); /** @@ -101,6 +106,8 @@ struct class *drm_sysfs_create(struct module *owner, char *name) if (err) goto err_out_class; + class->nodename = drm_nodename; + return class; err_out_class: diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index e9b436d2d94..9e9421525fb 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -850,8 +850,14 @@ static const struct file_operations hiddev_fops = { #endif }; +static char *hiddev_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + static struct usb_class_driver hiddev_class = { .name = "hiddev%d", + .nodename = hiddev_nodename, .fops = &hiddev_fops, .minor_base = HIDDEV_MINOR_BASE, }; @@ -955,7 +961,6 @@ static int hiddev_usbd_probe(struct usb_interface *intf, return -ENODEV; } - static /* const */ struct usb_driver hiddev_driver = { .name = "hiddev", .probe = hiddev_usbd_probe, diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d73f5f473e3..f8090e137fe 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -306,11 +306,11 @@ config SENSORS_F71805F will be called f71805f. config SENSORS_F71882FG - tristate "Fintek F71862FG, F71882FG and F8000" + tristate "Fintek F71858FG, F71862FG, F71882FG and F8000" depends on EXPERIMENTAL help If you say yes here you get support for hardware monitoring - features of the Fintek F71882FG/F71883FG, F71862FG/71863FG + features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG and F8000 Super-I/O chips. This driver can also be built as a module. If so, the module @@ -418,7 +418,7 @@ config SENSORS_IBMAEM power sensors and capping hardware in various IBM System X servers that support Active Energy Manager. This includes the x3350, x3550, x3650, x3655, x3755, x3850 M2, x3950 M2, - and certain HS2x/LS2x/QS2x blades. + and certain HC10/HS2x/LS2x/QS2x blades. This driver can also be built as a module. If so, the module will be called ibmaem. @@ -787,6 +787,16 @@ config SENSORS_THMC50 This driver can also be built as a module. If so, the module will be called thmc50. +config SENSORS_TMP401 + tristate "Texas Instruments TMP401 and compatibles" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for Texas Instruments TMP401 and + TMP411 temperature sensor chips. + + This driver can also be built as a module. If so, the module + will be called tmp401. + config SENSORS_VIA686A tristate "VIA686A" depends on PCI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 0ae26984ba4..b793dce6bed 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o obj-$(CONFIG_SENSORS_THMC50) += thmc50.o +obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_VT1211) += vt1211.o obj-$(CONFIG_SENSORS_VT8231) += vt8231.o diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 5f81ddf7150..4146105f1a5 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1,6 +1,6 @@ /*************************************************************************** * Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> * - * Copyright (C) 2007,2008 by Hans de Goede <hdegoede@redhat.com> * + * Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.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 * @@ -32,6 +32,7 @@ #define DRVNAME "f71882fg" +#define SIO_F71858FG_LD_HWM 0x02 /* Hardware monitor logical device */ #define SIO_F71882FG_LD_HWM 0x04 /* Hardware monitor logical device */ #define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ #define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */ @@ -44,6 +45,7 @@ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ #define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */ +#define SIO_F71858_ID 0x0507 /* Chipset ID */ #define SIO_F71862_ID 0x0601 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ #define SIO_F8000_ID 0x0581 /* Chipset ID */ @@ -70,6 +72,7 @@ #define F71882FG_REG_TEMP_HIGH(nr) (0x81 + 2 * (nr)) #define F71882FG_REG_TEMP_STATUS 0x62 #define F71882FG_REG_TEMP_BEEP 0x63 +#define F71882FG_REG_TEMP_CONFIG 0x69 #define F71882FG_REG_TEMP_HYST(nr) (0x6C + (nr)) #define F71882FG_REG_TEMP_TYPE 0x6B #define F71882FG_REG_TEMP_DIODE_OPEN 0x6F @@ -92,9 +95,10 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -enum chips { f71862fg, f71882fg, f8000 }; +enum chips { f71858fg, f71862fg, f71882fg, f8000 }; static const char *f71882fg_names[] = { + "f71858fg", "f71862fg", "f71882fg", "f8000", @@ -119,6 +123,7 @@ struct f71882fg_data { struct device *hwmon_dev; struct mutex update_lock; + int temp_start; /* temp numbering start (0 or 1) */ char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ unsigned long last_limits; /* In jiffies */ @@ -136,7 +141,7 @@ struct f71882fg_data { /* Note: all models have only 3 temperature channels, but on some they are addressed as 0-2 and on others as 1-3, so for coding convenience we reserve space for 4 channels */ - u8 temp[4]; + u16 temp[4]; u8 temp_ovt[4]; u8 temp_high[4]; u8 temp_hyst[2]; /* 2 hysts stored per reg */ @@ -144,6 +149,7 @@ struct f71882fg_data { u8 temp_status; u8 temp_beep; u8 temp_diode_open; + u8 temp_config; u8 pwm[4]; u8 pwm_enable; u8 pwm_auto_point_hyst[2]; @@ -247,11 +253,55 @@ static struct platform_driver f71882fg_driver = { .name = DRVNAME, }, .probe = f71882fg_probe, - .remove = __devexit_p(f71882fg_remove), + .remove = f71882fg_remove, }; static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +/* Temp and in attr for the f71858fg */ +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), + SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), + SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), + SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 0), + SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 0), + SENSOR_ATTR_2(temp1_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 0), + SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 0), + SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 0), + SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4), + SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0), + SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1), + SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 1), + SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 1), + SENSOR_ATTR_2(temp2_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), + SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 1), + SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 1), + SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), + SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1), + SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), + SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), + SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 2), + SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 2), + SENSOR_ATTR_2(temp3_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), + SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 2), + SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 2), + SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6), + 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[] = { SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), @@ -344,6 +394,7 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = { SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, store_temp_max, 0, 0), SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4), + SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0), SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1), SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_crit, store_temp_crit, 0, 1), @@ -351,12 +402,14 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = { store_temp_max, 0, 1), SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1), + SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_crit, store_temp_crit, 0, 2), SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, store_temp_max, 0, 2), SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6), + SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), }; /* Fan / PWM attr common to all models */ @@ -395,6 +448,9 @@ static struct sensor_device_attribute_2 fxxxx_fan_attr[] = { show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 1), + 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, @@ -450,9 +506,6 @@ 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, 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_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 1, 2), @@ -473,22 +526,8 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 3, 2), }; -/* Fan / PWM attr for the f71882fg */ -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), - +/* Fan / PWM attr common to both the f71882fg and f71858fg */ +static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = { SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 0), @@ -565,9 +604,6 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), - 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_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 2), @@ -605,6 +641,24 @@ static struct sensor_device_attribute_2 f71882fg_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, @@ -659,8 +713,6 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { static struct sensor_device_attribute_2 f8000_fan_attr[] = { SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), - SENSOR_ATTR_2(pwm3, S_IRUGO, show_pwm, NULL, 0, 2), - SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 2), @@ -857,13 +909,20 @@ static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val) outb(val & 255, data->addr + DATA_REG_OFFSET); } +static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr) +{ + if (data->type == f71858fg) + return f71882fg_read16(data, F71882FG_REG_TEMP(nr)); + else + return f71882fg_read8(data, F71882FG_REG_TEMP(nr)); +} + static struct f71882fg_data *f71882fg_update_device(struct device *dev) { struct f71882fg_data *data = dev_get_drvdata(dev); int nr, reg = 0, reg2; int nr_fans = (data->type == f71882fg) ? 4 : 3; - int nr_ins = (data->type == f8000) ? 3 : 9; - int temp_start = (data->type == f8000) ? 0 : 1; + int nr_ins = (data->type == f71858fg || data->type == f8000) ? 3 : 9; mutex_lock(&data->update_lock); @@ -878,7 +937,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) } /* Get High & boundary temps*/ - for (nr = temp_start; nr < 3 + temp_start; nr++) { + for (nr = data->temp_start; nr < 3 + data->temp_start; nr++) { data->temp_ovt[nr] = f71882fg_read8(data, F71882FG_REG_TEMP_OVT(nr)); data->temp_high[nr] = f71882fg_read8(data, @@ -886,14 +945,17 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) } if (data->type != f8000) { - data->fan_beep = f71882fg_read8(data, - F71882FG_REG_FAN_BEEP); - data->temp_beep = f71882fg_read8(data, - F71882FG_REG_TEMP_BEEP); data->temp_hyst[0] = f71882fg_read8(data, F71882FG_REG_TEMP_HYST(0)); data->temp_hyst[1] = f71882fg_read8(data, F71882FG_REG_TEMP_HYST(1)); + } + + if (data->type == f71862fg || data->type == f71882fg) { + data->fan_beep = f71882fg_read8(data, + F71882FG_REG_FAN_BEEP); + data->temp_beep = f71882fg_read8(data, + F71882FG_REG_TEMP_BEEP); /* Have to hardcode type, because temp1 is special */ reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); data->temp_type[2] = (reg & 0x04) ? 2 : 4; @@ -904,10 +966,10 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) data->temp_type[1] = 6 /* PECI */; else if ((reg2 & 0x03) == 0x02) data->temp_type[1] = 5 /* AMDSI */; - else if (data->type != f8000) + else if (data->type == f71862fg || data->type == f71882fg) data->temp_type[1] = (reg & 0x02) ? 2 : 4; else - data->temp_type[1] = 2; /* F8000 only supports BJT */ + data->temp_type[1] = 2; /* Only supports BJT */ data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); @@ -963,9 +1025,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) F71882FG_REG_TEMP_STATUS); data->temp_diode_open = f71882fg_read8(data, F71882FG_REG_TEMP_DIODE_OPEN); - for (nr = temp_start; nr < 3 + temp_start; nr++) - data->temp[nr] = f71882fg_read8(data, - F71882FG_REG_TEMP(nr)); + for (nr = data->temp_start; nr < 3 + data->temp_start; nr++) + data->temp[nr] = f71882fg_read_temp(data, nr); data->fan_status = f71882fg_read8(data, F71882FG_REG_FAN_STATUS); @@ -1168,8 +1229,24 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, { struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; + int sign, temp; + + if (data->type == f71858fg) { + /* TEMP_TABLE_SEL 1 or 3 ? */ + if (data->temp_config & 1) { + sign = data->temp[nr] & 0x0001; + temp = (data->temp[nr] >> 5) & 0x7ff; + } else { + sign = data->temp[nr] & 0x8000; + temp = (data->temp[nr] >> 5) & 0x3ff; + } + temp *= 125; + if (sign) + temp -= 128000; + } else + temp = data->temp[nr] * 1000; - return sprintf(buf, "%d\n", data->temp[nr] * 1000); + return sprintf(buf, "%d\n", temp); } static ssize_t show_temp_max(struct device *dev, struct device_attribute @@ -1440,6 +1517,10 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute int nr = to_sensor_dev_attr_2(devattr)->index; long val = simple_strtol(buf, NULL, 10); + /* Special case for F8000 pwm channel 3 which only does auto mode */ + if (data->type == f8000 && nr == 2 && val != 2) + return -EINVAL; + mutex_lock(&data->update_lock); data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); /* Special case for F8000 auto PWM mode / Thermostat mode */ @@ -1458,6 +1539,12 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute } else { switch (val) { case 1: + /* The f71858fg does not support manual RPM mode */ + if (data->type == f71858fg && + ((data->pwm_enable >> (2 * nr)) & 1)) { + count = -EINVAL; + goto leave; + } data->pwm_enable |= 2 << (2 * nr); break; /* Manual */ case 2: @@ -1616,9 +1703,9 @@ static ssize_t show_pwm_auto_point_channel(struct device *dev, int result; struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - int temp_start = (data->type == f8000) ? 0 : 1; - result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - temp_start); + result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - + data->temp_start); return sprintf(buf, "%d\n", result); } @@ -1629,7 +1716,6 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev, { struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - int temp_start = (data->type == f8000) ? 0 : 1; long val = simple_strtol(buf, NULL, 10); switch (val) { @@ -1645,7 +1731,7 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev, default: return -EINVAL; } - val += temp_start; + val += data->temp_start; mutex_lock(&data->update_lock); data->pwm_auto_point_mapping[nr] = f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); @@ -1721,6 +1807,8 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; data->type = sio_data->type; + data->temp_start = + (data->type == f71858fg || data->type == f8000) ? 0 : 1; mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); @@ -1736,19 +1824,6 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) goto exit_free; } - data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - /* If it is a 71862 and the fan / pwm part is enabled sanity check - the pwm settings */ - if (data->type == f71862fg && (start_reg & 0x02)) { - if ((data->pwm_enable & 0x15) != 0x15) { - dev_err(&pdev->dev, - "Invalid (reserved) pwm settings: 0x%02x\n", - (unsigned int)data->pwm_enable); - err = -ENODEV; - goto exit_free; - } - } - /* Register sysfs interface files */ err = device_create_file(&pdev->dev, &dev_attr_name); if (err) @@ -1756,6 +1831,20 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) if (start_reg & 0x01) { switch (data->type) { + case f71858fg: + data->temp_config = + f71882fg_read8(data, F71882FG_REG_TEMP_CONFIG); + if (data->temp_config & 0x10) + /* The f71858fg temperature alarms behave as + the f8000 alarms in this mode */ + err = f71882fg_create_sysfs_files(pdev, + f8000_in_temp_attr, + ARRAY_SIZE(f8000_in_temp_attr)); + else + err = f71882fg_create_sysfs_files(pdev, + f71858fg_in_temp_attr, + ARRAY_SIZE(f71858fg_in_temp_attr)); + break; case f71882fg: err = f71882fg_create_sysfs_files(pdev, f71882fg_in_temp_attr, @@ -1779,6 +1868,35 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) } if (start_reg & 0x02) { + data->pwm_enable = + f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); + + /* Sanity check the pwm settings */ + switch (data->type) { + case f71858fg: + err = 0; + for (i = 0; i < nr_fans; i++) + if (((data->pwm_enable >> (i * 2)) & 3) == 3) + err = 1; + break; + case f71862fg: + err = (data->pwm_enable & 0x15) != 0x15; + break; + case f71882fg: + err = 0; + break; + case f8000: + err = data->pwm_enable & 0x20; + break; + } + if (err) { + dev_err(&pdev->dev, + "Invalid (reserved) pwm settings: 0x%02x\n", + (unsigned int)data->pwm_enable); + err = -ENODEV; + goto exit_unregister_sysfs; + } + err = f71882fg_create_sysfs_files(pdev, fxxxx_fan_attr, ARRAY_SIZE(fxxxx_fan_attr)); if (err) @@ -1794,6 +1912,13 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) err = f71882fg_create_sysfs_files(pdev, f71882fg_fan_attr, ARRAY_SIZE(f71882fg_fan_attr)); + if (err) + goto exit_unregister_sysfs; + /* fall through! */ + case f71858fg: + err = f71882fg_create_sysfs_files(pdev, + f71882fg_f71858fg_fan_attr, + ARRAY_SIZE(f71882fg_f71858fg_fan_attr)); break; case f8000: err = f71882fg_create_sysfs_files(pdev, @@ -1878,6 +2003,9 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address, devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID); switch (devid) { + case SIO_F71858_ID: + sio_data->type = f71858fg; + break; case SIO_F71862_ID: sio_data->type = f71862fg; break; @@ -1892,7 +2020,11 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address, goto exit; } - superio_select(sioaddr, SIO_F71882FG_LD_HWM); + if (sio_data->type == f71858fg) + superio_select(sioaddr, SIO_F71858FG_LD_HWM); + else + superio_select(sioaddr, SIO_F71882FG_LD_HWM); + if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { printk(KERN_WARNING DRVNAME ": Device not activated\n"); goto exit; diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index e15c3e7b07e..29ea6753f3b 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -18,6 +18,7 @@ #include <linux/hwmon.h> #include <linux/gfp.h> #include <linux/spinlock.h> +#include <linux/pci.h> #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" @@ -86,8 +87,36 @@ void hwmon_device_unregister(struct device *dev) "hwmon_device_unregister() failed: bad class ID!\n"); } +static void __init hwmon_pci_quirks(void) +{ +#if defined CONFIG_X86 && defined CONFIG_PCI + struct pci_dev *sb; + u16 base; + u8 enable; + + /* Open access to 0x295-0x296 on MSI MS-7031 */ + sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL); + if (sb && + (sb->subsystem_vendor == 0x1462 && /* MSI */ + sb->subsystem_device == 0x0031)) { /* MS-7031 */ + + pci_read_config_byte(sb, 0x48, &enable); + pci_read_config_word(sb, 0x64, &base); + + if (base == 0 && !(enable & BIT(2))) { + dev_info(&sb->dev, + "Opening wide generic port at 0x295\n"); + pci_write_config_word(sb, 0x64, 0x295); + pci_write_config_byte(sb, 0x48, enable | BIT(2)); + } + } +#endif +} + static int __init hwmon_init(void) { + hwmon_pci_quirks(); + hwmon_class = class_create(THIS_MODULE, "hwmon"); if (IS_ERR(hwmon_class)) { printk(KERN_ERR "hwmon.c: couldn't create sysfs class\n"); diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index fe74609a7fe..405d3fb5d76 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -1127,3 +1127,4 @@ MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*"); MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*"); MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*"); MODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMBladeHC10-*"); diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index f27af6a9da4..86142a85823 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -12,7 +12,7 @@ * also work with the MAX6651. It does not distinguish max6650 and max6651 * chips. * - * Tha datasheet was last seen at: + * The datasheet was last seen at: * * http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf * @@ -98,6 +98,16 @@ I2C_CLIENT_INSMOD_1(max6650); #define MAX6650_CFG_MODE_OPEN_LOOP 0x30 #define MAX6650_COUNT_MASK 0x03 +/* + * Alarm status register bits + */ + +#define MAX6650_ALRM_MAX 0x01 +#define MAX6650_ALRM_MIN 0x02 +#define MAX6650_ALRM_TACH 0x04 +#define MAX6650_ALRM_GPIO1 0x08 +#define MAX6650_ALRM_GPIO2 0x10 + /* Minimum and maximum values of the FAN-RPM */ #define FAN_RPM_MIN 240 #define FAN_RPM_MAX 30000 @@ -151,6 +161,7 @@ struct max6650_data u8 tach[4]; u8 count; u8 dac; + u8 alarm; }; static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, @@ -418,6 +429,33 @@ static ssize_t set_div(struct device *dev, struct device_attribute *devattr, return count; } +/* + * Get alarm stati: + * Possible values: + * 0 = no alarm + * 1 = alarm + */ + +static ssize_t get_alarm(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max6650_data *data = max6650_update_device(dev); + struct i2c_client *client = to_i2c_client(dev); + int alarm = 0; + + if (data->alarm & attr->index) { + mutex_lock(&data->update_lock); + alarm = 1; + data->alarm &= ~attr->index; + data->alarm |= i2c_smbus_read_byte_data(client, + MAX6650_REG_ALARM); + mutex_unlock(&data->update_lock); + } + + return sprintf(buf, "%d\n", alarm); +} + static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0); static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1); static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2); @@ -426,7 +464,41 @@ static DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target, set_target); static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_div, set_div); static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_enable, set_enable); static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm); +static SENSOR_DEVICE_ATTR(fan1_max_alarm, S_IRUGO, get_alarm, NULL, + MAX6650_ALRM_MAX); +static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, get_alarm, NULL, + MAX6650_ALRM_MIN); +static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_alarm, NULL, + MAX6650_ALRM_TACH); +static SENSOR_DEVICE_ATTR(gpio1_alarm, S_IRUGO, get_alarm, NULL, + MAX6650_ALRM_GPIO1); +static SENSOR_DEVICE_ATTR(gpio2_alarm, S_IRUGO, get_alarm, NULL, + MAX6650_ALRM_GPIO2); + +static mode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a, + int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + u8 alarm_en = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN); + struct device_attribute *devattr; + /* + * Hide the alarms that have not been enabled by the firmware + */ + + devattr = container_of(a, struct device_attribute, attr); + if (devattr == &sensor_dev_attr_fan1_max_alarm.dev_attr + || devattr == &sensor_dev_attr_fan1_min_alarm.dev_attr + || devattr == &sensor_dev_attr_fan1_fault.dev_attr + || devattr == &sensor_dev_attr_gpio1_alarm.dev_attr + || devattr == &sensor_dev_attr_gpio2_alarm.dev_attr) { + if (!(alarm_en & to_sensor_dev_attr(devattr)->index)) + return 0; + } + + return a->mode; +} static struct attribute *max6650_attrs[] = { &sensor_dev_attr_fan1_input.dev_attr.attr, @@ -437,11 +509,17 @@ static struct attribute *max6650_attrs[] = { &dev_attr_fan1_div.attr, &dev_attr_pwm1_enable.attr, &dev_attr_pwm1.attr, + &sensor_dev_attr_fan1_max_alarm.dev_attr.attr, + &sensor_dev_attr_fan1_min_alarm.dev_attr.attr, + &sensor_dev_attr_fan1_fault.dev_attr.attr, + &sensor_dev_attr_gpio1_alarm.dev_attr.attr, + &sensor_dev_attr_gpio2_alarm.dev_attr.attr, NULL }; static struct attribute_group max6650_attr_grp = { .attrs = max6650_attrs, + .is_visible = max6650_attrs_visible, }; /* @@ -659,6 +737,12 @@ static struct max6650_data *max6650_update_device(struct device *dev) MAX6650_REG_COUNT); data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC); + /* Alarms are cleared on read in case the condition that + * caused the alarm is removed. Keep the value latched here + * for providing the register through different alarm files. */ + data->alarm |= i2c_smbus_read_byte_data(client, + MAX6650_REG_ALARM); + data->last_updated = jiffies; data->valid = 1; } diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 6cbdc2fea73..56cd6004da3 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -627,35 +627,35 @@ static struct platform_driver sht_drivers[] = { .owner = THIS_MODULE, }, .probe = sht15_probe, - .remove = sht15_remove, + .remove = __devexit_p(sht15_remove), }, { .driver = { .name = "sht11", .owner = THIS_MODULE, }, .probe = sht15_probe, - .remove = sht15_remove, + .remove = __devexit_p(sht15_remove), }, { .driver = { .name = "sht15", .owner = THIS_MODULE, }, .probe = sht15_probe, - .remove = sht15_remove, + .remove = __devexit_p(sht15_remove), }, { .driver = { .name = "sht71", .owner = THIS_MODULE, }, .probe = sht15_probe, - .remove = sht15_remove, + .remove = __devexit_p(sht15_remove), }, { .driver = { .name = "sht75", .owner = THIS_MODULE, }, .probe = sht15_probe, - .remove = sht15_remove, + .remove = __devexit_p(sht15_remove), }, }; diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c new file mode 100644 index 00000000000..7b34f2cd08b --- /dev/null +++ b/drivers/hwmon/tmp401.c @@ -0,0 +1,690 @@ +/* tmp401.c + * + * Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com> + * Preliminary tmp411 support by: + * Gabriel Konat, Sander Leget, Wouter Willems + * Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de> + * + * 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. + */ + +/* + * Driver for the Texas Instruments TMP401 SMBUS temperature sensor IC. + * + * Note this IC is in some aspect similar to the LM90, but it has quite a + * few differences too, for example the local temp has a higher resolution + * and thus has 16 bits registers for its value and limit instead of 8 bits. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/sysfs.h> + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_2(tmp401, tmp411); + +/* + * The TMP401 registers, note some registers have different addresses for + * reading and writing + */ +#define TMP401_STATUS 0x02 +#define TMP401_CONFIG_READ 0x03 +#define TMP401_CONFIG_WRITE 0x09 +#define TMP401_CONVERSION_RATE_READ 0x04 +#define TMP401_CONVERSION_RATE_WRITE 0x0A +#define TMP401_TEMP_CRIT_HYST 0x21 +#define TMP401_CONSECUTIVE_ALERT 0x22 +#define TMP401_MANUFACTURER_ID_REG 0xFE +#define TMP401_DEVICE_ID_REG 0xFF +#define TMP411_N_FACTOR_REG 0x18 + +static const u8 TMP401_TEMP_MSB[2] = { 0x00, 0x01 }; +static const u8 TMP401_TEMP_LSB[2] = { 0x15, 0x10 }; +static const u8 TMP401_TEMP_LOW_LIMIT_MSB_READ[2] = { 0x06, 0x08 }; +static const u8 TMP401_TEMP_LOW_LIMIT_MSB_WRITE[2] = { 0x0C, 0x0E }; +static const u8 TMP401_TEMP_LOW_LIMIT_LSB[2] = { 0x17, 0x14 }; +static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_READ[2] = { 0x05, 0x07 }; +static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[2] = { 0x0B, 0x0D }; +static const u8 TMP401_TEMP_HIGH_LIMIT_LSB[2] = { 0x16, 0x13 }; +/* These are called the THERM limit / hysteresis / mask in the datasheet */ +static const u8 TMP401_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 }; + +static const u8 TMP411_TEMP_LOWEST_MSB[2] = { 0x30, 0x34 }; +static const u8 TMP411_TEMP_LOWEST_LSB[2] = { 0x31, 0x35 }; +static const u8 TMP411_TEMP_HIGHEST_MSB[2] = { 0x32, 0x36 }; +static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 }; + +/* Flags */ +#define TMP401_CONFIG_RANGE 0x04 +#define TMP401_CONFIG_SHUTDOWN 0x40 +#define TMP401_STATUS_LOCAL_CRIT 0x01 +#define TMP401_STATUS_REMOTE_CRIT 0x02 +#define TMP401_STATUS_REMOTE_OPEN 0x04 +#define TMP401_STATUS_REMOTE_LOW 0x08 +#define TMP401_STATUS_REMOTE_HIGH 0x10 +#define TMP401_STATUS_LOCAL_LOW 0x20 +#define TMP401_STATUS_LOCAL_HIGH 0x40 + +/* Manufacturer / Device ID's */ +#define TMP401_MANUFACTURER_ID 0x55 +#define TMP401_DEVICE_ID 0x11 +#define TMP411_DEVICE_ID 0x12 + +/* + * Functions declarations + */ + +static int tmp401_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int tmp401_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info); +static int tmp401_remove(struct i2c_client *client); +static struct tmp401_data *tmp401_update_device(struct device *dev); + +/* + * Driver data (common to all clients) + */ + +static const struct i2c_device_id tmp401_id[] = { + { "tmp401", tmp401 }, + { "tmp411", tmp411 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tmp401_id); + +static struct i2c_driver tmp401_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "tmp401", + }, + .probe = tmp401_probe, + .remove = tmp401_remove, + .id_table = tmp401_id, + .detect = tmp401_detect, + .address_data = &addr_data, +}; + +/* + * Client data (each client gets its own) + */ + +struct tmp401_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + int kind; + + /* register values */ + u8 status; + u8 config; + u16 temp[2]; + u16 temp_low[2]; + u16 temp_high[2]; + u8 temp_crit[2]; + u8 temp_crit_hyst; + u16 temp_lowest[2]; + u16 temp_highest[2]; +}; + +/* + * Sysfs attr show / store functions + */ + +static int tmp401_register_to_temp(u16 reg, u8 config) +{ + int temp = reg; + + if (config & TMP401_CONFIG_RANGE) + temp -= 64 * 256; + + return (temp * 625 + 80) / 160; +} + +static u16 tmp401_temp_to_register(long temp, u8 config) +{ + if (config & TMP401_CONFIG_RANGE) { + temp = SENSORS_LIMIT(temp, -64000, 191000); + temp += 64000; + } else + temp = SENSORS_LIMIT(temp, 0, 127000); + + return (temp * 160 + 312) / 625; +} + +static int tmp401_crit_register_to_temp(u8 reg, u8 config) +{ + int temp = reg; + + if (config & TMP401_CONFIG_RANGE) + temp -= 64; + + return temp * 1000; +} + +static u8 tmp401_crit_temp_to_register(long temp, u8 config) +{ + if (config & TMP401_CONFIG_RANGE) { + temp = SENSORS_LIMIT(temp, -64000, 191000); + temp += 64000; + } else + temp = SENSORS_LIMIT(temp, 0, 127000); + + return (temp + 500) / 1000; +} + +static ssize_t show_temp_value(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + + return sprintf(buf, "%d\n", + tmp401_register_to_temp(data->temp[index], data->config)); +} + +static ssize_t show_temp_min(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + + return sprintf(buf, "%d\n", + tmp401_register_to_temp(data->temp_low[index], data->config)); +} + +static ssize_t show_temp_max(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + + return sprintf(buf, "%d\n", + tmp401_register_to_temp(data->temp_high[index], data->config)); +} + +static ssize_t show_temp_crit(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + + return sprintf(buf, "%d\n", + tmp401_crit_register_to_temp(data->temp_crit[index], + data->config)); +} + +static ssize_t show_temp_crit_hyst(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int temp, index = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + + mutex_lock(&data->update_lock); + temp = tmp401_crit_register_to_temp(data->temp_crit[index], + data->config); + temp -= data->temp_crit_hyst * 1000; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", temp); +} + +static ssize_t show_temp_lowest(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + + return sprintf(buf, "%d\n", + tmp401_register_to_temp(data->temp_lowest[index], + data->config)); +} + +static ssize_t show_temp_highest(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + + return sprintf(buf, "%d\n", + tmp401_register_to_temp(data->temp_highest[index], + data->config)); +} + +static ssize_t show_status(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int mask = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + + if (data->status & mask) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t store_temp_min(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + long val; + u16 reg; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + reg = tmp401_temp_to_register(val, data->config); + + mutex_lock(&data->update_lock); + + i2c_smbus_write_byte_data(to_i2c_client(dev), + TMP401_TEMP_LOW_LIMIT_MSB_WRITE[index], reg >> 8); + i2c_smbus_write_byte_data(to_i2c_client(dev), + TMP401_TEMP_LOW_LIMIT_LSB[index], reg & 0xFF); + + data->temp_low[index] = reg; + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t store_temp_max(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + long val; + u16 reg; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + reg = tmp401_temp_to_register(val, data->config); + + mutex_lock(&data->update_lock); + + i2c_smbus_write_byte_data(to_i2c_client(dev), + TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[index], reg >> 8); + i2c_smbus_write_byte_data(to_i2c_client(dev), + TMP401_TEMP_HIGH_LIMIT_LSB[index], reg & 0xFF); + + data->temp_high[index] = reg; + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t store_temp_crit(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + long val; + u8 reg; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + reg = tmp401_crit_temp_to_register(val, data->config); + + mutex_lock(&data->update_lock); + + i2c_smbus_write_byte_data(to_i2c_client(dev), + TMP401_TEMP_CRIT_LIMIT[index], reg); + + data->temp_crit[index] = reg; + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + int temp, index = to_sensor_dev_attr(devattr)->index; + struct tmp401_data *data = tmp401_update_device(dev); + long val; + u8 reg; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + if (data->config & TMP401_CONFIG_RANGE) + val = SENSORS_LIMIT(val, -64000, 191000); + else + val = SENSORS_LIMIT(val, 0, 127000); + + mutex_lock(&data->update_lock); + temp = tmp401_crit_register_to_temp(data->temp_crit[index], + data->config); + val = SENSORS_LIMIT(val, temp - 255000, temp); + reg = ((temp - val) + 500) / 1000; + + i2c_smbus_write_byte_data(to_i2c_client(dev), + TMP401_TEMP_CRIT_HYST, reg); + + data->temp_crit_hyst = reg; + + mutex_unlock(&data->update_lock); + + return count; +} + +/* + * Resets the historical measurements of minimum and maximum temperatures. + * This is done by writing any value to any of the minimum/maximum registers + * (0x30-0x37). + */ +static ssize_t reset_temp_history(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) +{ + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + if (val != 1) { + dev_err(dev, "temp_reset_history value %ld not" + " supported. Use 1 to reset the history!\n", val); + return -EINVAL; + } + i2c_smbus_write_byte_data(to_i2c_client(dev), + TMP411_TEMP_LOWEST_MSB[0], val); + + return count; +} + +static struct sensor_device_attribute tmp401_attr[] = { + SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0), + SENSOR_ATTR(temp1_min, 0644, show_temp_min, store_temp_min, 0), + SENSOR_ATTR(temp1_max, 0644, show_temp_max, store_temp_max, 0), + SENSOR_ATTR(temp1_crit, 0644, show_temp_crit, store_temp_crit, 0), + SENSOR_ATTR(temp1_crit_hyst, 0644, show_temp_crit_hyst, + store_temp_crit_hyst, 0), + SENSOR_ATTR(temp1_min_alarm, 0444, show_status, NULL, + TMP401_STATUS_LOCAL_LOW), + SENSOR_ATTR(temp1_max_alarm, 0444, show_status, NULL, + TMP401_STATUS_LOCAL_HIGH), + SENSOR_ATTR(temp1_crit_alarm, 0444, show_status, NULL, + TMP401_STATUS_LOCAL_CRIT), + SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1), + SENSOR_ATTR(temp2_min, 0644, show_temp_min, store_temp_min, 1), + SENSOR_ATTR(temp2_max, 0644, show_temp_max, store_temp_max, 1), + SENSOR_ATTR(temp2_crit, 0644, show_temp_crit, store_temp_crit, 1), + SENSOR_ATTR(temp2_crit_hyst, 0444, show_temp_crit_hyst, NULL, 1), + SENSOR_ATTR(temp2_fault, 0444, show_status, NULL, + TMP401_STATUS_REMOTE_OPEN), + SENSOR_ATTR(temp2_min_alarm, 0444, show_status, NULL, + TMP401_STATUS_REMOTE_LOW), + SENSOR_ATTR(temp2_max_alarm, 0444, show_status, NULL, + TMP401_STATUS_REMOTE_HIGH), + SENSOR_ATTR(temp2_crit_alarm, 0444, show_status, NULL, + TMP401_STATUS_REMOTE_CRIT), +}; + +/* + * Additional features of the TMP411 chip. + * The TMP411 stores the minimum and maximum + * temperature measured since power-on, chip-reset, or + * minimum and maximum register reset for both the local + * and remote channels. + */ +static struct sensor_device_attribute tmp411_attr[] = { + SENSOR_ATTR(temp1_highest, 0444, show_temp_highest, NULL, 0), + SENSOR_ATTR(temp1_lowest, 0444, show_temp_lowest, NULL, 0), + SENSOR_ATTR(temp2_highest, 0444, show_temp_highest, NULL, 1), + SENSOR_ATTR(temp2_lowest, 0444, show_temp_lowest, NULL, 1), + SENSOR_ATTR(temp_reset_history, 0200, NULL, reset_temp_history, 0), +}; + +/* + * Begin non sysfs callback code (aka Real code) + */ + +static void tmp401_init_client(struct i2c_client *client) +{ + int config, config_orig; + + /* Set the conversion rate to 2 Hz */ + i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5); + + /* Start conversions (disable shutdown if necessary) */ + config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); + if (config < 0) { + dev_warn(&client->dev, "Initialization failed!\n"); + return; + } + + config_orig = config; + config &= ~TMP401_CONFIG_SHUTDOWN; + + if (config != config_orig) + i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config); +} + +static int tmp401_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + 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_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; +} + +static int tmp401_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i, err = 0; + struct tmp401_data *data; + const char *names[] = { "TMP401", "TMP411" }; + + data = kzalloc(sizeof(struct tmp401_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->kind = id->driver_data; + + /* Initialize the TMP401 chip */ + tmp401_init_client(client); + + /* Register sysfs hooks */ + for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) { + err = device_create_file(&client->dev, + &tmp401_attr[i].dev_attr); + if (err) + goto exit_remove; + } + + /* Register aditional tmp411 sysfs hooks */ + if (data->kind == tmp411) { + for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) { + err = device_create_file(&client->dev, + &tmp411_attr[i].dev_attr); + if (err) + goto exit_remove; + } + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + data->hwmon_dev = NULL; + goto exit_remove; + } + + dev_info(&client->dev, "Detected TI %s chip\n", + names[data->kind - 1]); + + return 0; + +exit_remove: + tmp401_remove(client); /* will also free data for us */ + return err; +} + +static int tmp401_remove(struct i2c_client *client) +{ + struct tmp401_data *data = i2c_get_clientdata(client); + int i; + + if (data->hwmon_dev) + hwmon_device_unregister(data->hwmon_dev); + + for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) + device_remove_file(&client->dev, &tmp401_attr[i].dev_attr); + + if (data->kind == tmp411) { + for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) + device_remove_file(&client->dev, + &tmp411_attr[i].dev_attr); + } + + kfree(data); + return 0; +} + +static struct tmp401_data *tmp401_update_device_reg16( + struct i2c_client *client, struct tmp401_data *data) +{ + int i; + + for (i = 0; i < 2; i++) { + /* + * High byte must be read first immediately followed + * by the low byte + */ + data->temp[i] = i2c_smbus_read_byte_data(client, + TMP401_TEMP_MSB[i]) << 8; + data->temp[i] |= i2c_smbus_read_byte_data(client, + TMP401_TEMP_LSB[i]); + data->temp_low[i] = i2c_smbus_read_byte_data(client, + TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8; + data->temp_low[i] |= i2c_smbus_read_byte_data(client, + TMP401_TEMP_LOW_LIMIT_LSB[i]); + data->temp_high[i] = i2c_smbus_read_byte_data(client, + TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8; + data->temp_high[i] |= i2c_smbus_read_byte_data(client, + TMP401_TEMP_HIGH_LIMIT_LSB[i]); + data->temp_crit[i] = i2c_smbus_read_byte_data(client, + TMP401_TEMP_CRIT_LIMIT[i]); + + if (data->kind == tmp411) { + data->temp_lowest[i] = i2c_smbus_read_byte_data(client, + TMP411_TEMP_LOWEST_MSB[i]) << 8; + data->temp_lowest[i] |= i2c_smbus_read_byte_data( + client, TMP411_TEMP_LOWEST_LSB[i]); + + data->temp_highest[i] = i2c_smbus_read_byte_data( + client, TMP411_TEMP_HIGHEST_MSB[i]) << 8; + data->temp_highest[i] |= i2c_smbus_read_byte_data( + client, TMP411_TEMP_HIGHEST_LSB[i]); + } + } + return data; +} + +static struct tmp401_data *tmp401_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tmp401_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS); + data->config = i2c_smbus_read_byte_data(client, + TMP401_CONFIG_READ); + tmp401_update_device_reg16(client, data); + + data->temp_crit_hyst = i2c_smbus_read_byte_data(client, + TMP401_TEMP_CRIT_HYST); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init tmp401_init(void) +{ + return i2c_add_driver(&tmp401_driver); +} + +static void __exit tmp401_exit(void) +{ + i2c_del_driver(&tmp401_driver); +} + +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("Texas Instruments TMP401 temperature sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(tmp401_init); +module_exit(tmp401_exit); diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index e64b42058b2..0e9746913d2 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -36,6 +36,7 @@ w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3 0x8860 0xa1 w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 + w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3 w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3 */ @@ -53,12 +54,13 @@ #include <asm/io.h> #include "lm75.h" -enum kinds { w83627ehf, w83627dhg, w83667hg }; +enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg }; /* used to set data->name = w83627ehf_device_names[data->sio_kind] */ static const char * w83627ehf_device_names[] = { "w83627ehf", "w83627dhg", + "w83627dhg", "w83667hg", }; @@ -86,6 +88,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); #define SIO_W83627EHF_ID 0x8850 #define SIO_W83627EHG_ID 0x8860 #define SIO_W83627DHG_ID 0xa020 +#define SIO_W83627DHG_P_ID 0xb070 #define SIO_W83667HG_ID 0xa510 #define SIO_ID_MASK 0xFFF0 @@ -1517,6 +1520,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, static const char __initdata sio_name_W83627EHF[] = "W83627EHF"; static const char __initdata sio_name_W83627EHG[] = "W83627EHG"; static const char __initdata sio_name_W83627DHG[] = "W83627DHG"; + static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P"; static const char __initdata sio_name_W83667HG[] = "W83667HG"; u16 val; @@ -1542,6 +1546,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, sio_data->kind = w83627dhg; sio_name = sio_name_W83627DHG; break; + case SIO_W83627DHG_P_ID: + sio_data->kind = w83627dhg_p; + sio_name = sio_name_W83627DHG_P; + break; case SIO_W83667HG_ID: sio_data->kind = w83667hg; sio_name = sio_name_W83667HG; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index c8460fa9cfa..0d04d3ebfc2 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -211,7 +211,7 @@ config I2C_VIA will be called i2c-via. config I2C_VIAPRO - tristate "VIA VT82C596/82C686/82xx and CX700/VX800/VX820" + tristate "VIA VT82C596/82C686/82xx and CX700/VX8xx" depends on PCI help If you say yes to this option, support will be included for the VIA @@ -225,8 +225,8 @@ config I2C_VIAPRO VT8237R/A/S VT8251 CX700 - VX800 - VX820 + VX800/VX820 + VX855/VX875 This driver can also be built as a module. If so, the module will be called i2c-viapro. diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index 02e6f724b05..54d810a4d00 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -37,6 +37,7 @@ VT8251 0x3287 yes CX700 0x8324 yes VX800/VX820 0x8353 yes + VX855/VX875 0x8409 yes Note: we assume there can only be one device, with one SMBus interface. */ @@ -404,6 +405,7 @@ found: switch (pdev->device) { case PCI_DEVICE_ID_VIA_CX700: case PCI_DEVICE_ID_VIA_VX800: + case PCI_DEVICE_ID_VIA_VX855: case PCI_DEVICE_ID_VIA_8251: case PCI_DEVICE_ID_VIA_8237: case PCI_DEVICE_ID_VIA_8237A: @@ -469,6 +471,8 @@ static struct pci_device_id vt596_ids[] = { .driver_data = SMBBA3 }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800), .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855), + .driver_data = SMBBA3 }, { 0, } }; diff --git a/drivers/i2c/busses/i2c-voodoo3.c b/drivers/i2c/busses/i2c-voodoo3.c index 1a474acc0dd..7663d57833a 100644 --- a/drivers/i2c/busses/i2c-voodoo3.c +++ b/drivers/i2c/busses/i2c-voodoo3.c @@ -163,7 +163,6 @@ static struct i2c_algo_bit_data voo_i2c_bit_data = { static struct i2c_adapter voodoo3_i2c_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_TV_ANALOG, .name = "I2C Voodoo3/Banshee adapter", .algo_data = &voo_i2c_bit_data, }; diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 8f8c81eb0ae..02d746c9c47 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -64,21 +64,6 @@ config SENSORS_PCA9539 This driver is deprecated and will be dropped soon. Use drivers/gpio/pca953x.c instead. -config SENSORS_MAX6875 - tristate "Maxim MAX6875 Power supply supervisor" - depends on EXPERIMENTAL - help - If you say yes here you get support for the Maxim MAX6875 - EEPROM-programmable, quad power-supply sequencer/supervisor. - - This provides an interface to program the EEPROM and reset the chip. - - This driver also supports the Maxim MAX6874 hex power-supply - sequencer/supervisor if found at a compatible address. - - This driver can also be built as a module. If so, the module - will be called max6875. - 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 55a37603718..f4680d16ee3 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -11,7 +11,6 @@ # obj-$(CONFIG_DS1682) += ds1682.o -obj-$(CONFIG_SENSORS_MAX6875) += max6875.o obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o obj-$(CONFIG_PCF8575) += pcf8575.o diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 85e2e919d1c..5ed622ee65c 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -29,7 +29,6 @@ #include <linux/i2c.h> #include <linux/init.h> #include <linux/idr.h> -#include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/completion.h> #include <linux/hardirq.h> @@ -451,16 +450,6 @@ static int i2c_register_adapter(struct i2c_adapter *adap) mutex_lock(&core_lock); - /* Add the adapter to the driver core. - * If the parent pointer is not set up, - * we add this adapter to the host bus. - */ - if (adap->dev.parent == NULL) { - adap->dev.parent = &platform_bus; - pr_debug("I2C adapter driver [%s] forgot to specify " - "physical device\n", adap->name); - } - /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) adap->timeout = HZ; @@ -1022,7 +1011,8 @@ module_exit(i2c_exit); */ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - int ret; + unsigned long orig_jiffies; + int ret, try; /* REVISIT the fault reporting model here is weak: * @@ -1060,7 +1050,15 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) mutex_lock_nested(&adap->bus_lock, adap->level); } - ret = adap->algo->master_xfer(adap,msgs,num); + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (ret = 0, try = 0; try <= adap->retries; try++) { + ret = adap->algo->master_xfer(adap, msgs, num); + if (ret != -EAGAIN) + break; + if (time_after(jiffies, orig_jiffies + adap->timeout)) + break; + } mutex_unlock(&adap->bus_lock); return ret; @@ -1509,7 +1507,7 @@ struct i2c_adapter* i2c_get_adapter(int id) struct i2c_adapter *adapter; mutex_lock(&core_lock); - adapter = (struct i2c_adapter *)idr_find(&i2c_adapter_idr, id); + adapter = idr_find(&i2c_adapter_idr, id); if (adapter && !try_module_get(adapter->owner)) adapter = NULL; @@ -1995,14 +1993,27 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, union i2c_smbus_data *data) { + unsigned long orig_jiffies; + int try; s32 res; flags &= I2C_M_TEN | I2C_CLIENT_PEC; if (adapter->algo->smbus_xfer) { mutex_lock(&adapter->bus_lock); - res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write, - command, protocol, data); + + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (res = 0, try = 0; try <= adapter->retries; try++) { + res = adapter->algo->smbus_xfer(adapter, addr, flags, + read_write, command, + protocol, data); + if (res != -EAGAIN) + break; + if (time_after(jiffies, + orig_jiffies + adapter->timeout)) + break; + } mutex_unlock(&adapter->bus_lock); } else res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c index ba1488bd843..c14ca144cff 100644 --- a/drivers/ide/ide-pm.c +++ b/drivers/ide/ide-pm.c @@ -3,7 +3,8 @@ int generic_ide_suspend(struct device *dev, pm_message_t mesg) { - ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive); + ide_drive_t *drive = dev_get_drvdata(dev); + ide_drive_t *pair = ide_get_pair_dev(drive); ide_hwif_t *hwif = drive->hwif; struct request *rq; struct request_pm_state rqpm; @@ -34,7 +35,8 @@ int generic_ide_suspend(struct device *dev, pm_message_t mesg) int generic_ide_resume(struct device *dev) { - ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive); + ide_drive_t *drive = dev_get_drvdata(dev); + ide_drive_t *pair = ide_get_pair_dev(drive); ide_hwif_t *hwif = drive->hwif; struct request *rq; struct request_pm_state rqpm; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index f371b0de314..79e0af3fd15 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -535,7 +535,7 @@ static int ide_register_port(ide_hwif_t *hwif) /* register with global device tree */ dev_set_name(&hwif->gendev, hwif->name); - hwif->gendev.driver_data = hwif; + dev_set_drvdata(&hwif->gendev, hwif); if (hwif->gendev.parent == NULL) hwif->gendev.parent = hwif->dev; hwif->gendev.release = hwif_release_dev; @@ -987,9 +987,9 @@ static void hwif_register_devices(ide_hwif_t *hwif) int ret; dev_set_name(dev, "%u.%u", hwif->index, i); + dev_set_drvdata(dev, drive); dev->parent = &hwif->gendev; dev->bus = &ide_bus_type; - dev->driver_data = drive; dev->release = drive_release_dev; ret = device_register(dev); diff --git a/drivers/ide/ide_platform.c b/drivers/ide/ide_platform.c index ee9b55ecc62..b579fbe8837 100644 --- a/drivers/ide/ide_platform.c +++ b/drivers/ide/ide_platform.c @@ -112,7 +112,7 @@ out: static int __devexit plat_ide_remove(struct platform_device *pdev) { - struct ide_host *host = pdev->dev.driver_data; + struct ide_host *host = dev_get_drvdata(&pdev->dev); ide_host_remove(host); diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c index 4ca103577c0..f5c586c2bba 100644 --- a/drivers/ieee1394/eth1394.c +++ b/drivers/ieee1394/eth1394.c @@ -361,7 +361,7 @@ static int eth1394_new_node(struct eth1394_host_info *hi, node_info->pdg.sz = 0; node_info->fifo = CSR1212_INVALID_ADDR_SPACE; - ud->device.driver_data = node_info; + dev_set_drvdata(&ud->device, node_info); new_node->ud = ud; priv = netdev_priv(hi->dev); @@ -406,7 +406,7 @@ static int eth1394_remove(struct device *dev) list_del(&old_node->list); kfree(old_node); - node_info = (struct eth1394_node_info*)ud->device.driver_data; + node_info = dev_get_drvdata(&ud->device); spin_lock_irqsave(&node_info->pdg.lock, flags); /* The partial datagram list should be empty, but we'll just @@ -416,7 +416,7 @@ static int eth1394_remove(struct device *dev) spin_unlock_irqrestore(&node_info->pdg.lock, flags); kfree(node_info); - ud->device.driver_data = NULL; + dev_set_drvdata(&ud->device, NULL); return 0; } @@ -688,7 +688,7 @@ static void ether1394_host_reset(struct hpsb_host *host) ether1394_reset_priv(dev, 0); list_for_each_entry(node, &priv->ip_node_list, list) { - node_info = node->ud->device.driver_data; + node_info = dev_get_drvdata(&node->ud->device); spin_lock_irqsave(&node_info->pdg.lock, flags); @@ -872,8 +872,7 @@ static __be16 ether1394_parse_encap(struct sk_buff *skb, struct net_device *dev, if (!node) return cpu_to_be16(0); - node_info = - (struct eth1394_node_info *)node->ud->device.driver_data; + node_info = dev_get_drvdata(&node->ud->device); /* Update our speed/payload/fifo_offset table */ node_info->maxpayload = maxpayload; @@ -1080,7 +1079,7 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, priv->ud_list[NODEID_TO_NODE(srcid)] = ud; } - node_info = (struct eth1394_node_info *)ud->device.driver_data; + node_info = dev_get_drvdata(&ud->device); /* First, did we receive a fragmented or unfragmented datagram? */ hdr->words.word1 = ntohs(hdr->words.word1); @@ -1617,8 +1616,7 @@ static int ether1394_tx(struct sk_buff *skb, struct net_device *dev) if (!node) goto fail; - node_info = - (struct eth1394_node_info *)node->ud->device.driver_data; + node_info = dev_get_drvdata(&node->ud->device); if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE) goto fail; diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index a51ab233342..83b734aec92 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -718,7 +718,7 @@ static int sbp2_remove(struct device *dev) struct scsi_device *sdev; ud = container_of(dev, struct unit_directory, device); - lu = ud->device.driver_data; + lu = dev_get_drvdata(&ud->device); if (!lu) return 0; @@ -746,7 +746,7 @@ static int sbp2_remove(struct device *dev) static int sbp2_update(struct unit_directory *ud) { - struct sbp2_lu *lu = ud->device.driver_data; + struct sbp2_lu *lu = dev_get_drvdata(&ud->device); if (sbp2_reconnect_device(lu) != 0) { /* @@ -815,7 +815,7 @@ static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *ud) atomic_set(&lu->state, SBP2LU_STATE_RUNNING); INIT_WORK(&lu->protocol_work, NULL); - ud->device.driver_data = lu; + dev_set_drvdata(&ud->device, lu); hi = hpsb_get_hostinfo(&sbp2_highlevel, ud->ne->host); if (!hi) { @@ -1051,7 +1051,7 @@ static void sbp2_remove_device(struct sbp2_lu *lu) hpsb_unregister_addrspace(&sbp2_highlevel, hi->host, lu->status_fifo_addr); - lu->ud->device.driver_data = NULL; + dev_set_drvdata(&lu->ud->device, NULL); module_put(hi->host->driver->owner); no_hi: diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 5c04cfb54cb..158a214da2f 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -760,9 +760,9 @@ int ib_device_register_sysfs(struct ib_device *device) int i; class_dev->class = &ib_class; - class_dev->driver_data = device; class_dev->parent = device->dma_device; dev_set_name(class_dev, device->name); + dev_set_drvdata(class_dev, device); INIT_LIST_HEAD(&device->port_list); diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c index 85905ab9391..ce4e6eff479 100644 --- a/drivers/infiniband/hw/ehca/ehca_main.c +++ b/drivers/infiniband/hw/ehca/ehca_main.c @@ -636,7 +636,7 @@ static ssize_t ehca_show_##name(struct device *dev, \ struct hipz_query_hca *rblock; \ int data; \ \ - shca = dev->driver_data; \ + shca = dev_get_drvdata(dev); \ \ rblock = ehca_alloc_fw_ctrlblock(GFP_KERNEL); \ if (!rblock) { \ @@ -680,7 +680,7 @@ static ssize_t ehca_show_adapter_handle(struct device *dev, struct device_attribute *attr, char *buf) { - struct ehca_shca *shca = dev->driver_data; + struct ehca_shca *shca = dev_get_drvdata(dev); return sprintf(buf, "%llx\n", shca->ipz_hca_handle.handle); @@ -749,7 +749,7 @@ static int __devinit ehca_probe(struct of_device *dev, shca->ofdev = dev; shca->ipz_hca_handle.handle = *handle; - dev->dev.driver_data = shca; + dev_set_drvdata(&dev->dev, shca); ret = ehca_sense_attributes(shca); if (ret < 0) { @@ -878,7 +878,7 @@ probe1: static int __devexit ehca_remove(struct of_device *dev) { - struct ehca_shca *shca = dev->dev.driver_data; + struct ehca_shca *shca = dev_get_drvdata(&dev->dev); unsigned long flags; int ret; diff --git a/drivers/input/input.c b/drivers/input/input.c index 5d445f48789..7c237e6ac71 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1265,8 +1265,14 @@ static struct device_type input_dev_type = { .uevent = input_dev_uevent, }; +static char *input_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev)); +} + struct class input_class = { .name = "input", + .nodename = input_nodename, }; EXPORT_SYMBOL_GPL(input_class); diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 69af8385ab1..2957d48e004 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -569,7 +569,7 @@ static int wm97xx_probe(struct device *dev) mutex_init(&wm->codec_mutex); wm->dev = dev; - dev->driver_data = wm; + dev_set_drvdata(dev, wm); wm->ac97 = to_ac97_t(dev); /* check that we have a supported codec */ diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c index 928d2ed8865..b115726dc08 100644 --- a/drivers/input/xen-kbdfront.c +++ b/drivers/input/xen-kbdfront.c @@ -114,7 +114,7 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); return -ENOMEM; } - dev->dev.driver_data = info; + dev_set_drvdata(&dev->dev, info); info->xbdev = dev; info->irq = -1; snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename); @@ -186,7 +186,7 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, static int xenkbd_resume(struct xenbus_device *dev) { - struct xenkbd_info *info = dev->dev.driver_data; + struct xenkbd_info *info = dev_get_drvdata(&dev->dev); xenkbd_disconnect_backend(info); memset(info->page, 0, PAGE_SIZE); @@ -195,7 +195,7 @@ static int xenkbd_resume(struct xenbus_device *dev) static int xenkbd_remove(struct xenbus_device *dev) { - struct xenkbd_info *info = dev->dev.driver_data; + struct xenkbd_info *info = dev_get_drvdata(&dev->dev); xenkbd_disconnect_backend(info); if (info->kbd) @@ -266,7 +266,7 @@ static void xenkbd_disconnect_backend(struct xenkbd_info *info) static void xenkbd_backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { - struct xenkbd_info *info = dev->dev.driver_data; + struct xenkbd_info *info = dev_get_drvdata(&dev->dev); int ret, val; switch (backend_state) { diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index 0ddf9044948..fde377c60cc 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -72,7 +72,7 @@ MODULE_PARM_DESC(verbose,"Verbose log operations " "(default 0)"); struct thermostat { - struct i2c_client clt; + struct i2c_client *clt; u8 temps[3]; u8 cached_temp[3]; u8 initial_limits[3]; @@ -87,9 +87,6 @@ static struct of_device * of_dev; static struct thermostat* thermostat; static struct task_struct *thread_therm = NULL; -static int attach_one_thermostat(struct i2c_adapter *adapter, int addr, - int busno); - static void write_both_fan_speed(struct thermostat *th, int speed); static void write_fan_speed(struct thermostat *th, int speed, int fan); @@ -101,7 +98,7 @@ write_reg(struct thermostat* th, int reg, u8 data) tmp[0] = reg; tmp[1] = data; - rc = i2c_master_send(&th->clt, (const char *)tmp, 2); + rc = i2c_master_send(th->clt, (const char *)tmp, 2); if (rc < 0) return rc; if (rc != 2) @@ -116,12 +113,12 @@ read_reg(struct thermostat* th, int reg) int rc; reg_addr = (u8)reg; - rc = i2c_master_send(&th->clt, ®_addr, 1); + rc = i2c_master_send(th->clt, ®_addr, 1); if (rc < 0) return rc; if (rc != 1) return -ENODEV; - rc = i2c_master_recv(&th->clt, (char *)&data, 1); + rc = i2c_master_recv(th->clt, (char *)&data, 1); if (rc < 0) return rc; return data; @@ -131,26 +128,36 @@ static int attach_thermostat(struct i2c_adapter *adapter) { unsigned long bus_no; + struct i2c_board_info info; + struct i2c_client *client; if (strncmp(adapter->name, "uni-n", 5)) return -ENODEV; bus_no = simple_strtoul(adapter->name + 6, NULL, 10); if (bus_no != therm_bus) return -ENODEV; - return attach_one_thermostat(adapter, therm_address, bus_no); + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "therm_adt746x", I2C_NAME_SIZE); + info.addr = therm_address; + client = i2c_new_device(adapter, &info); + if (!client) + return -ENODEV; + + /* + * Let i2c-core delete that device on driver removal. + * This is safe because i2c-core holds the core_lock mutex for us. + */ + list_add_tail(&client->detected, &client->driver->clients); + return 0; } static int -detach_thermostat(struct i2c_adapter *adapter) +remove_thermostat(struct i2c_client *client) { - struct thermostat* th; + struct thermostat *th = i2c_get_clientdata(client); int i; - if (thermostat == NULL) - return 0; - - th = thermostat; - if (thread_therm != NULL) { kthread_stop(thread_therm); } @@ -166,8 +173,6 @@ detach_thermostat(struct i2c_adapter *adapter) write_both_fan_speed(th, -1); - i2c_detach_client(&th->clt); - thermostat = NULL; kfree(th); @@ -175,14 +180,6 @@ detach_thermostat(struct i2c_adapter *adapter) return 0; } -static struct i2c_driver thermostat_driver = { - .driver = { - .name = "therm_adt746x", - }, - .attach_adapter = attach_thermostat, - .detach_adapter = detach_thermostat, -}; - static int read_fan_speed(struct thermostat *th, u8 addr) { u8 tmp[2]; @@ -371,8 +368,8 @@ static void set_limit(struct thermostat *th, int i) th->limits[i] = default_limits_local[i] + limit_adjust; } -static int attach_one_thermostat(struct i2c_adapter *adapter, int addr, - int busno) +static int probe_thermostat(struct i2c_client *client, + const struct i2c_device_id *id) { struct thermostat* th; int rc; @@ -385,16 +382,12 @@ static int attach_one_thermostat(struct i2c_adapter *adapter, int addr, if (!th) return -ENOMEM; - th->clt.addr = addr; - th->clt.adapter = adapter; - th->clt.driver = &thermostat_driver; - strcpy(th->clt.name, "thermostat"); + i2c_set_clientdata(client, th); + th->clt = client; rc = read_reg(th, 0); if (rc < 0) { - printk(KERN_ERR "adt746x: Thermostat failed to read config " - "from bus %d !\n", - busno); + dev_err(&client->dev, "Thermostat failed to read config!\n"); kfree(th); return -ENODEV; } @@ -423,14 +416,6 @@ static int attach_one_thermostat(struct i2c_adapter *adapter, int addr, thermostat = th; - if (i2c_attach_client(&th->clt)) { - printk(KERN_INFO "adt746x: Thermostat failed to attach " - "client !\n"); - thermostat = NULL; - kfree(th); - return -ENODEV; - } - /* be sure to really write fan speed the first time */ th->last_speed[0] = -2; th->last_speed[1] = -2; @@ -456,6 +441,21 @@ static int attach_one_thermostat(struct i2c_adapter *adapter, int addr, return 0; } +static const struct i2c_device_id therm_adt746x_id[] = { + { "therm_adt746x", 0 }, + { } +}; + +static struct i2c_driver thermostat_driver = { + .driver = { + .name = "therm_adt746x", + }, + .attach_adapter = attach_thermostat, + .probe = probe_thermostat, + .remove = remove_thermostat, + .id_table = therm_adt746x_id, +}; + /* * Now, unfortunately, sysfs doesn't give us a nice void * we could * pass around to the attribute functions, so we don't really have diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index 817607e2af6..a028598af2d 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -287,22 +287,6 @@ struct fcu_fan_table fcu_fans[] = { }; /* - * i2c_driver structure to attach to the host i2c controller - */ - -static int therm_pm72_attach(struct i2c_adapter *adapter); -static int therm_pm72_detach(struct i2c_adapter *adapter); - -static struct i2c_driver therm_pm72_driver = -{ - .driver = { - .name = "therm_pm72", - }, - .attach_adapter = therm_pm72_attach, - .detach_adapter = therm_pm72_detach, -}; - -/* * Utility function to create an i2c_client structure and * attach it to one of u3 adapters */ @@ -310,6 +294,7 @@ static struct i2c_client *attach_i2c_chip(int id, const char *name) { struct i2c_client *clt; struct i2c_adapter *adap; + struct i2c_board_info info; if (id & 0x200) adap = k2; @@ -320,31 +305,21 @@ static struct i2c_client *attach_i2c_chip(int id, const char *name) if (adap == NULL) return NULL; - clt = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (clt == NULL) - return NULL; - - clt->addr = (id >> 1) & 0x7f; - clt->adapter = adap; - clt->driver = &therm_pm72_driver; - strncpy(clt->name, name, I2C_NAME_SIZE-1); - - if (i2c_attach_client(clt)) { + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = (id >> 1) & 0x7f; + strlcpy(info.type, "therm_pm72", I2C_NAME_SIZE); + clt = i2c_new_device(adap, &info); + if (!clt) { printk(KERN_ERR "therm_pm72: Failed to attach to i2c ID 0x%x\n", id); - kfree(clt); return NULL; } - return clt; -} -/* - * Utility function to get rid of the i2c_client structure - * (will also detach from the adapter hopepfully) - */ -static void detach_i2c_chip(struct i2c_client *clt) -{ - i2c_detach_client(clt); - kfree(clt); + /* + * Let i2c-core delete that device on driver removal. + * This is safe because i2c-core holds the core_lock mutex for us. + */ + list_add_tail(&clt->detected, &clt->driver->clients); + return clt; } /* @@ -1203,8 +1178,6 @@ static int init_cpu_state(struct cpu_pid_state *state, int index) return 0; fail: - if (state->monitor) - detach_i2c_chip(state->monitor); state->monitor = NULL; return -ENODEV; @@ -1232,7 +1205,6 @@ static void dispose_cpu_state(struct cpu_pid_state *state) device_remove_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm); } - detach_i2c_chip(state->monitor); state->monitor = NULL; } @@ -1407,7 +1379,6 @@ static void dispose_backside_state(struct backside_pid_state *state) device_remove_file(&of_dev->dev, &dev_attr_backside_temperature); device_remove_file(&of_dev->dev, &dev_attr_backside_fan_pwm); - detach_i2c_chip(state->monitor); state->monitor = NULL; } @@ -1532,7 +1503,6 @@ static void dispose_drives_state(struct drives_pid_state *state) device_remove_file(&of_dev->dev, &dev_attr_drives_temperature); device_remove_file(&of_dev->dev, &dev_attr_drives_fan_rpm); - detach_i2c_chip(state->monitor); state->monitor = NULL; } @@ -1654,7 +1624,6 @@ static void dispose_dimms_state(struct dimm_pid_state *state) device_remove_file(&of_dev->dev, &dev_attr_dimms_temperature); - detach_i2c_chip(state->monitor); state->monitor = NULL; } @@ -1779,7 +1748,6 @@ static void dispose_slots_state(struct slots_pid_state *state) device_remove_file(&of_dev->dev, &dev_attr_slots_temperature); device_remove_file(&of_dev->dev, &dev_attr_slots_fan_pwm); - detach_i2c_chip(state->monitor); state->monitor = NULL; } @@ -2008,8 +1976,6 @@ static int attach_fcu(void) */ static void detach_fcu(void) { - if (fcu) - detach_i2c_chip(fcu); fcu = NULL; } @@ -2060,12 +2026,21 @@ static int therm_pm72_attach(struct i2c_adapter *adapter) return 0; } +static int therm_pm72_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + /* Always succeed, the real work was done in therm_pm72_attach() */ + return 0; +} + /* - * Called on every adapter when the driver or the i2c controller + * Called when any of the devices which participates into thermal management * is going away. */ -static int therm_pm72_detach(struct i2c_adapter *adapter) +static int therm_pm72_remove(struct i2c_client *client) { + struct i2c_adapter *adapter = client->adapter; + mutex_lock(&driver_lock); if (state != state_detached) @@ -2096,6 +2071,30 @@ static int therm_pm72_detach(struct i2c_adapter *adapter) return 0; } +/* + * i2c_driver structure to attach to the host i2c controller + */ + +static const struct i2c_device_id therm_pm72_id[] = { + /* + * Fake device name, thermal management is done by several + * chips but we don't need to differentiate between them at + * this point. + */ + { "therm_pm72", 0 }, + { } +}; + +static struct i2c_driver therm_pm72_driver = { + .driver = { + .name = "therm_pm72", + }, + .attach_adapter = therm_pm72_attach, + .probe = therm_pm72_probe, + .remove = therm_pm72_remove, + .id_table = therm_pm72_id, +}; + static int fan_check_loc_match(const char *loc, int fan) { char tmp[64]; diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index 3da0a02efd7..40023313a76 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -48,16 +48,6 @@ #define LOG_TEMP 0 /* continously log temperature */ -static int do_probe( struct i2c_adapter *adapter, int addr, int kind); - -/* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */ -static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, - 0x4c, 0x4d, 0x4e, 0x4f, - 0x2c, 0x2d, 0x2e, 0x2f, - I2C_CLIENT_END }; - -I2C_CLIENT_INSMOD; - static struct { volatile int running; struct task_struct *poll_task; @@ -315,53 +305,54 @@ static int control_loop(void *dummy) static int do_attach( struct i2c_adapter *adapter ) { - int ret = 0; + /* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */ + static const unsigned short scan_ds1775[] = { + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + I2C_CLIENT_END + }; + static const unsigned short scan_adm1030[] = { + 0x2c, 0x2d, 0x2e, 0x2f, + I2C_CLIENT_END + }; if( strncmp(adapter->name, "uni-n", 5) ) return 0; if( !x.running ) { - ret = i2c_probe( adapter, &addr_data, &do_probe ); + struct i2c_board_info info; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "therm_ds1775", I2C_NAME_SIZE); + i2c_new_probed_device(adapter, &info, scan_ds1775); + + strlcpy(info.type, "therm_adm1030", I2C_NAME_SIZE); + i2c_new_probed_device(adapter, &info, scan_adm1030); + if( x.thermostat && x.fan ) { x.running = 1; x.poll_task = kthread_run(control_loop, NULL, "g4fand"); } } - return ret; + return 0; } static int -do_detach( struct i2c_client *client ) +do_remove(struct i2c_client *client) { - int err; - - if( (err=i2c_detach_client(client)) ) - printk(KERN_ERR "failed to detach thermostat client\n"); - else { - if( x.running ) { - x.running = 0; - kthread_stop(x.poll_task); - x.poll_task = NULL; - } - if( client == x.thermostat ) - x.thermostat = NULL; - else if( client == x.fan ) - x.fan = NULL; - else { - printk(KERN_ERR "g4fan: bad client\n"); - } - kfree( client ); + if (x.running) { + x.running = 0; + kthread_stop(x.poll_task); + x.poll_task = NULL; } - return err; -} + if (client == x.thermostat) + x.thermostat = NULL; + else if (client == x.fan) + x.fan = NULL; + else + printk(KERN_ERR "g4fan: bad client\n"); -static struct i2c_driver g4fan_driver = { - .driver = { - .name = "therm_windtunnel", - }, - .attach_adapter = do_attach, - .detach_client = do_detach, -}; + return 0; +} static int attach_fan( struct i2c_client *cl ) @@ -374,13 +365,8 @@ attach_fan( struct i2c_client *cl ) goto out; printk("ADM1030 fan controller [@%02x]\n", cl->addr ); - strlcpy( cl->name, "ADM1030 fan controller", sizeof(cl->name) ); - - if( !i2c_attach_client(cl) ) - x.fan = cl; + x.fan = cl; out: - if( cl != x.fan ) - kfree( cl ); return 0; } @@ -412,39 +398,47 @@ attach_thermostat( struct i2c_client *cl ) x.temp = temp; x.overheat_temp = os_temp; x.overheat_hyst = hyst_temp; - - strlcpy( cl->name, "DS1775 thermostat", sizeof(cl->name) ); - - if( !i2c_attach_client(cl) ) - x.thermostat = cl; + x.thermostat = cl; out: - if( cl != x.thermostat ) - kfree( cl ); return 0; } +enum chip { ds1775, adm1030 }; + +static const struct i2c_device_id therm_windtunnel_id[] = { + { "therm_ds1775", ds1775 }, + { "therm_adm1030", adm1030 }, + { } +}; + static int -do_probe( struct i2c_adapter *adapter, int addr, int kind ) +do_probe(struct i2c_client *cl, const struct i2c_device_id *id) { - struct i2c_client *cl; + struct i2c_adapter *adapter = cl->adapter; if( !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_WRITE_BYTE) ) return 0; - if( !(cl=kzalloc(sizeof(*cl), GFP_KERNEL)) ) - return -ENOMEM; - - cl->addr = addr; - cl->adapter = adapter; - cl->driver = &g4fan_driver; - cl->flags = 0; - - if( addr < 0x48 ) + switch (id->driver_data) { + case adm1030: return attach_fan( cl ); - return attach_thermostat( cl ); + case ds1775: + return attach_thermostat(cl); + } + return 0; } +static struct i2c_driver g4fan_driver = { + .driver = { + .name = "therm_windtunnel", + }, + .attach_adapter = do_attach, + .probe = do_probe, + .remove = do_remove, + .id_table = therm_windtunnel_id, +}; + /************************************************************************/ /* initialization / cleanup */ diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c index b92b959fe16..529886c7a82 100644 --- a/drivers/macintosh/windfarm_lm75_sensor.c +++ b/drivers/macintosh/windfarm_lm75_sensor.c @@ -37,34 +37,22 @@ struct wf_lm75_sensor { int ds1775 : 1; int inited : 1; - struct i2c_client i2c; + struct i2c_client *i2c; struct wf_sensor sens; }; #define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens) -#define i2c_to_lm75(c) container_of(c, struct wf_lm75_sensor, i2c) - -static int wf_lm75_attach(struct i2c_adapter *adapter); -static int wf_lm75_detach(struct i2c_client *client); - -static struct i2c_driver wf_lm75_driver = { - .driver = { - .name = "wf_lm75", - }, - .attach_adapter = wf_lm75_attach, - .detach_client = wf_lm75_detach, -}; static int wf_lm75_get(struct wf_sensor *sr, s32 *value) { struct wf_lm75_sensor *lm = wf_to_lm75(sr); s32 data; - if (lm->i2c.adapter == NULL) + if (lm->i2c == NULL) return -ENODEV; /* Init chip if necessary */ if (!lm->inited) { - u8 cfg_new, cfg = (u8)i2c_smbus_read_byte_data(&lm->i2c, 1); + u8 cfg_new, cfg = (u8)i2c_smbus_read_byte_data(lm->i2c, 1); DBG("wf_lm75: Initializing %s, cfg was: %02x\n", sr->name, cfg); @@ -73,7 +61,7 @@ static int wf_lm75_get(struct wf_sensor *sr, s32 *value) * the firmware for now */ cfg_new = cfg & ~0x01; - i2c_smbus_write_byte_data(&lm->i2c, 1, cfg_new); + i2c_smbus_write_byte_data(lm->i2c, 1, cfg_new); lm->inited = 1; /* If we just powered it up, let's wait 200 ms */ @@ -81,7 +69,7 @@ static int wf_lm75_get(struct wf_sensor *sr, s32 *value) } /* Read temperature register */ - data = (s32)le16_to_cpu(i2c_smbus_read_word_data(&lm->i2c, 0)); + data = (s32)le16_to_cpu(i2c_smbus_read_word_data(lm->i2c, 0)); data <<= 8; *value = data; @@ -92,12 +80,6 @@ static void wf_lm75_release(struct wf_sensor *sr) { struct wf_lm75_sensor *lm = wf_to_lm75(sr); - /* check if client is registered and detach from i2c */ - if (lm->i2c.adapter) { - i2c_detach_client(&lm->i2c); - lm->i2c.adapter = NULL; - } - kfree(lm); } @@ -107,59 +89,77 @@ static struct wf_sensor_ops wf_lm75_ops = { .owner = THIS_MODULE, }; -static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter, - u8 addr, int ds1775, - const char *loc) +static int wf_lm75_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct wf_lm75_sensor *lm; int rc; - DBG("wf_lm75: creating %s device at address 0x%02x\n", - ds1775 ? "ds1775" : "lm75", addr); - lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL); if (lm == NULL) - return NULL; + return -ENODEV; + + lm->inited = 0; + lm->ds1775 = id->driver_data; + lm->i2c = client; + lm->sens.name = client->dev.platform_data; + lm->sens.ops = &wf_lm75_ops; + i2c_set_clientdata(client, lm); + + rc = wf_register_sensor(&lm->sens); + if (rc) { + i2c_set_clientdata(client, NULL); + kfree(lm); + } + + return rc; +} + +static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter, + u8 addr, int ds1775, + const char *loc) +{ + struct i2c_board_info info; + struct i2c_client *client; + char *name; + + DBG("wf_lm75: creating %s device at address 0x%02x\n", + ds1775 ? "ds1775" : "lm75", addr); /* Usual rant about sensor names not beeing very consistent in * the device-tree, oh well ... * Add more entries below as you deal with more setups */ if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY")) - lm->sens.name = "hd-temp"; + name = "hd-temp"; else if (!strcmp(loc, "Incoming Air Temp")) - lm->sens.name = "incoming-air-temp"; + name = "incoming-air-temp"; else if (!strcmp(loc, "ODD Temp")) - lm->sens.name = "optical-drive-temp"; + name = "optical-drive-temp"; else if (!strcmp(loc, "HD Temp")) - lm->sens.name = "hard-drive-temp"; + name = "hard-drive-temp"; else goto fail; - lm->inited = 0; - lm->sens.ops = &wf_lm75_ops; - lm->ds1775 = ds1775; - lm->i2c.addr = (addr >> 1) & 0x7f; - lm->i2c.adapter = adapter; - lm->i2c.driver = &wf_lm75_driver; - strncpy(lm->i2c.name, lm->sens.name, I2C_NAME_SIZE-1); - - rc = i2c_attach_client(&lm->i2c); - if (rc) { - printk(KERN_ERR "windfarm: failed to attach %s %s to i2c," - " err %d\n", ds1775 ? "ds1775" : "lm75", - lm->i2c.name, rc); - goto fail; - } + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = (addr >> 1) & 0x7f; + info.platform_data = name; + strlcpy(info.type, ds1775 ? "wf_ds1775" : "wf_lm75", I2C_NAME_SIZE); - if (wf_register_sensor(&lm->sens)) { - i2c_detach_client(&lm->i2c); + client = i2c_new_device(adapter, &info); + if (client == NULL) { + printk(KERN_ERR "windfarm: failed to attach %s %s to i2c\n", + ds1775 ? "ds1775" : "lm75", name); goto fail; } - return lm; + /* + * Let i2c-core delete that device on driver removal. + * This is safe because i2c-core holds the core_lock mutex for us. + */ + list_add_tail(&client->detected, &client->driver->clients); + return client; fail: - kfree(lm); return NULL; } @@ -202,21 +202,38 @@ static int wf_lm75_attach(struct i2c_adapter *adapter) return 0; } -static int wf_lm75_detach(struct i2c_client *client) +static int wf_lm75_remove(struct i2c_client *client) { - struct wf_lm75_sensor *lm = i2c_to_lm75(client); + struct wf_lm75_sensor *lm = i2c_get_clientdata(client); DBG("wf_lm75: i2c detatch called for %s\n", lm->sens.name); /* Mark client detached */ - lm->i2c.adapter = NULL; + lm->i2c = NULL; /* release sensor */ wf_unregister_sensor(&lm->sens); + i2c_set_clientdata(client, NULL); return 0; } +static const struct i2c_device_id wf_lm75_id[] = { + { "wf_lm75", 0 }, + { "wf_ds1775", 1 }, + { } +}; + +static struct i2c_driver wf_lm75_driver = { + .driver = { + .name = "wf_lm75", + }, + .attach_adapter = wf_lm75_attach, + .probe = wf_lm75_probe, + .remove = wf_lm75_remove, + .id_table = wf_lm75_id, +}; + static int __init wf_lm75_sensor_init(void) { /* Don't register on old machines that use therm_pm72 for now */ diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c index e207a90d6b2..e2a55ecda2b 100644 --- a/drivers/macintosh/windfarm_max6690_sensor.c +++ b/drivers/macintosh/windfarm_max6690_sensor.c @@ -26,34 +26,22 @@ #define MAX6690_EXTERNAL_TEMP 1 struct wf_6690_sensor { - struct i2c_client i2c; + struct i2c_client *i2c; struct wf_sensor sens; }; #define wf_to_6690(x) container_of((x), struct wf_6690_sensor, sens) -#define i2c_to_6690(x) container_of((x), struct wf_6690_sensor, i2c) - -static int wf_max6690_attach(struct i2c_adapter *adapter); -static int wf_max6690_detach(struct i2c_client *client); - -static struct i2c_driver wf_max6690_driver = { - .driver = { - .name = "wf_max6690", - }, - .attach_adapter = wf_max6690_attach, - .detach_client = wf_max6690_detach, -}; static int wf_max6690_get(struct wf_sensor *sr, s32 *value) { struct wf_6690_sensor *max = wf_to_6690(sr); s32 data; - if (max->i2c.adapter == NULL) + if (max->i2c == NULL) return -ENODEV; /* chip gets initialized by firmware */ - data = i2c_smbus_read_byte_data(&max->i2c, MAX6690_EXTERNAL_TEMP); + data = i2c_smbus_read_byte_data(max->i2c, MAX6690_EXTERNAL_TEMP); if (data < 0) return data; *value = data << 16; @@ -64,10 +52,6 @@ static void wf_max6690_release(struct wf_sensor *sr) { struct wf_6690_sensor *max = wf_to_6690(sr); - if (max->i2c.adapter) { - i2c_detach_client(&max->i2c); - max->i2c.adapter = NULL; - } kfree(max); } @@ -77,19 +61,40 @@ static struct wf_sensor_ops wf_max6690_ops = { .owner = THIS_MODULE, }; -static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr, - const char *loc) +static int wf_max6690_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct wf_6690_sensor *max; - char *name; + int rc; max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL); if (max == NULL) { - printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor %s: " - "no memory\n", loc); - return; + printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: " + "no memory\n"); + return -ENOMEM; + } + + max->i2c = client; + max->sens.name = client->dev.platform_data; + max->sens.ops = &wf_max6690_ops; + i2c_set_clientdata(client, max); + + rc = wf_register_sensor(&max->sens); + if (rc) { + i2c_set_clientdata(client, NULL); + kfree(max); } + return rc; +} + +static struct i2c_client *wf_max6690_create(struct i2c_adapter *adapter, + u8 addr, const char *loc) +{ + struct i2c_board_info info; + struct i2c_client *client; + char *name; + if (!strcmp(loc, "BACKSIDE")) name = "backside-temp"; else if (!strcmp(loc, "NB Ambient")) @@ -99,27 +104,26 @@ static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr, else goto fail; - max->sens.ops = &wf_max6690_ops; - max->sens.name = name; - max->i2c.addr = addr >> 1; - max->i2c.adapter = adapter; - max->i2c.driver = &wf_max6690_driver; - strncpy(max->i2c.name, name, I2C_NAME_SIZE-1); + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = addr >> 1; + info.platform_data = name; + strlcpy(info.type, "wf_max6690", I2C_NAME_SIZE); - if (i2c_attach_client(&max->i2c)) { + client = i2c_new_device(adapter, &info); + if (client == NULL) { printk(KERN_ERR "windfarm: failed to attach MAX6690 sensor\n"); goto fail; } - if (wf_register_sensor(&max->sens)) { - i2c_detach_client(&max->i2c); - goto fail; - } - - return; + /* + * Let i2c-core delete that device on driver removal. + * This is safe because i2c-core holds the core_lock mutex for us. + */ + list_add_tail(&client->detected, &client->driver->clients); + return client; fail: - kfree(max); + return NULL; } static int wf_max6690_attach(struct i2c_adapter *adapter) @@ -154,16 +158,31 @@ static int wf_max6690_attach(struct i2c_adapter *adapter) return 0; } -static int wf_max6690_detach(struct i2c_client *client) +static int wf_max6690_remove(struct i2c_client *client) { - struct wf_6690_sensor *max = i2c_to_6690(client); + struct wf_6690_sensor *max = i2c_get_clientdata(client); - max->i2c.adapter = NULL; + max->i2c = NULL; wf_unregister_sensor(&max->sens); return 0; } +static const struct i2c_device_id wf_max6690_id[] = { + { "wf_max6690", 0 }, + { } +}; + +static struct i2c_driver wf_max6690_driver = { + .driver = { + .name = "wf_max6690", + }, + .attach_adapter = wf_max6690_attach, + .probe = wf_max6690_probe, + .remove = wf_max6690_remove, + .id_table = wf_max6690_id, +}; + static int __init wf_max6690_sensor_init(void) { /* Don't register on old machines that use therm_pm72 for now */ diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c index 7847e981ac3..5da729e58f9 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c @@ -39,7 +39,7 @@ struct wf_sat { struct mutex mutex; unsigned long last_read; /* jiffies when cache last updated */ u8 cache[16]; - struct i2c_client i2c; + struct i2c_client *i2c; struct device_node *node; }; @@ -54,18 +54,6 @@ struct wf_sat_sensor { }; #define wf_to_sat(c) container_of(c, struct wf_sat_sensor, sens) -#define i2c_to_sat(c) container_of(c, struct wf_sat, i2c) - -static int wf_sat_attach(struct i2c_adapter *adapter); -static int wf_sat_detach(struct i2c_client *client); - -static struct i2c_driver wf_sat_driver = { - .driver = { - .name = "wf_smu_sat", - }, - .attach_adapter = wf_sat_attach, - .detach_client = wf_sat_detach, -}; struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id, unsigned int *size) @@ -81,13 +69,13 @@ struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id, if (sat_id > 1 || (sat = sats[sat_id]) == NULL) return NULL; - err = i2c_smbus_write_word_data(&sat->i2c, 8, id << 8); + err = i2c_smbus_write_word_data(sat->i2c, 8, id << 8); if (err) { printk(KERN_ERR "smu_sat_get_sdb_part wr error %d\n", err); return NULL; } - err = i2c_smbus_read_word_data(&sat->i2c, 9); + err = i2c_smbus_read_word_data(sat->i2c, 9); if (err < 0) { printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n"); return NULL; @@ -105,7 +93,7 @@ struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id, return NULL; for (i = 0; i < len; i += 4) { - err = i2c_smbus_read_i2c_block_data(&sat->i2c, 0xa, 4, data); + err = i2c_smbus_read_i2c_block_data(sat->i2c, 0xa, 4, data); if (err < 0) { printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n", err); @@ -138,7 +126,7 @@ static int wf_sat_read_cache(struct wf_sat *sat) { int err; - err = i2c_smbus_read_i2c_block_data(&sat->i2c, 0x3f, 16, sat->cache); + err = i2c_smbus_read_i2c_block_data(sat->i2c, 0x3f, 16, sat->cache); if (err < 0) return err; sat->last_read = jiffies; @@ -161,7 +149,7 @@ static int wf_sat_get(struct wf_sensor *sr, s32 *value) int i, err; s32 val; - if (sat->i2c.adapter == NULL) + if (sat->i2c == NULL) return -ENODEV; mutex_lock(&sat->mutex); @@ -193,10 +181,6 @@ static void wf_sat_release(struct wf_sensor *sr) struct wf_sat *sat = sens->sat; if (atomic_dec_and_test(&sat->refcnt)) { - if (sat->i2c.adapter) { - i2c_detach_client(&sat->i2c); - sat->i2c.adapter = NULL; - } if (sat->nr >= 0) sats[sat->nr] = NULL; kfree(sat); @@ -212,38 +196,58 @@ static struct wf_sensor_ops wf_sat_ops = { static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) { + struct i2c_board_info info; + struct i2c_client *client; + const u32 *reg; + u8 addr; + + reg = of_get_property(dev, "reg", NULL); + if (reg == NULL) + return; + addr = *reg; + DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr); + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = (addr >> 1) & 0x7f; + info.platform_data = dev; + strlcpy(info.type, "wf_sat", I2C_NAME_SIZE); + + client = i2c_new_device(adapter, &info); + if (client == NULL) { + printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n"); + return; + } + + /* + * Let i2c-core delete that device on driver removal. + * This is safe because i2c-core holds the core_lock mutex for us. + */ + list_add_tail(&client->detected, &client->driver->clients); +} + +static int wf_sat_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device_node *dev = client->dev.platform_data; struct wf_sat *sat; struct wf_sat_sensor *sens; const u32 *reg; const char *loc, *type; - u8 addr, chip, core; + u8 chip, core; struct device_node *child; int shift, cpu, index; char *name; int vsens[2], isens[2]; - reg = of_get_property(dev, "reg", NULL); - if (reg == NULL) - return; - addr = *reg; - DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr); - sat = kzalloc(sizeof(struct wf_sat), GFP_KERNEL); if (sat == NULL) - return; + return -ENOMEM; sat->nr = -1; sat->node = of_node_get(dev); atomic_set(&sat->refcnt, 0); mutex_init(&sat->mutex); - sat->i2c.addr = (addr >> 1) & 0x7f; - sat->i2c.adapter = adapter; - sat->i2c.driver = &wf_sat_driver; - strncpy(sat->i2c.name, "smu-sat", I2C_NAME_SIZE-1); - - if (i2c_attach_client(&sat->i2c)) { - printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n"); - goto fail; - } + sat->i2c = client; + i2c_set_clientdata(client, sat); vsens[0] = vsens[1] = -1; isens[0] = isens[1] = -1; @@ -344,10 +348,7 @@ static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) if (sat->nr >= 0) sats[sat->nr] = sat; - return; - - fail: - kfree(sat); + return 0; } static int wf_sat_attach(struct i2c_adapter *adapter) @@ -366,16 +367,32 @@ static int wf_sat_attach(struct i2c_adapter *adapter) return 0; } -static int wf_sat_detach(struct i2c_client *client) +static int wf_sat_remove(struct i2c_client *client) { - struct wf_sat *sat = i2c_to_sat(client); + struct wf_sat *sat = i2c_get_clientdata(client); /* XXX TODO */ - sat->i2c.adapter = NULL; + sat->i2c = NULL; + i2c_set_clientdata(client, NULL); return 0; } +static const struct i2c_device_id wf_sat_id[] = { + { "wf_sat", 0 }, + { } +}; + +static struct i2c_driver wf_sat_driver = { + .driver = { + .name = "wf_smu_sat", + }, + .attach_adapter = wf_sat_attach, + .probe = wf_sat_probe, + .remove = wf_sat_remove, + .id_table = wf_sat_id, +}; + static int __init sat_sensors_init(void) { return i2c_add_driver(&wf_sat_driver); diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 823ceba6efa..1128d3fba79 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1513,6 +1513,7 @@ static const struct file_operations _ctl_fops = { static struct miscdevice _dm_misc = { .minor = MISC_DYNAMIC_MINOR, .name = DM_NAME, + .devnode = "mapper/control", .fops = &_ctl_fops }; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 3fd8b1e6548..48db308fae6 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -19,7 +19,6 @@ #include <linux/slab.h> #include <linux/idr.h> #include <linux/hdreg.h> -#include <linux/blktrace_api.h> #include <trace/events/block.h> diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index 1adce9ff52c..7636c33bc1e 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -48,7 +48,7 @@ MODULE_PARM_DESC(audio_std, "NICAM/A\n" "NICAM/B\n"); -static char firmware_name[FIRMWARE_NAME_MAX]; +static char firmware_name[30]; module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0); MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the " "default firmware name\n"); diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c index a454ee8f1e4..479dd05762a 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.c +++ b/drivers/media/dvb/dvb-core/dvbdev.c @@ -447,6 +447,15 @@ static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } +static char *dvb_nodename(struct device *dev) +{ + struct dvb_device *dvbdev = dev_get_drvdata(dev); + + return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d", + dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id); +} + + static int __init init_dvbdev(void) { int retval; @@ -469,6 +478,7 @@ static int __init init_dvbdev(void) goto error; } dvb_class->dev_uevent = dvb_uevent; + dvb_class->nodename = dvb_nodename; return 0; error: diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index 2d5352e54dc..b5157518a30 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -196,7 +196,7 @@ struct dvb_usb_device_properties { #define CYPRESS_FX2 3 int usb_ctrl; int (*download_firmware) (struct usb_device *, const struct firmware *); - const char firmware[FIRMWARE_NAME_MAX]; + const char *firmware; int no_reconnect; int size_of_priv; diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c index 4e207658c5d..2b6eeeab5b2 100644 --- a/drivers/media/dvb/firewire/firedtv-1394.c +++ b/drivers/media/dvb/firewire/firedtv-1394.c @@ -225,7 +225,7 @@ fail_free: static int node_remove(struct device *dev) { - struct firedtv *fdtv = dev->driver_data; + struct firedtv *fdtv = dev_get_drvdata(dev); fdtv_dvb_unregister(fdtv); @@ -242,7 +242,7 @@ static int node_remove(struct device *dev) static int node_update(struct unit_directory *ud) { - struct firedtv *fdtv = ud->device.driver_data; + struct firedtv *fdtv = dev_get_drvdata(&ud->device); if (fdtv->isochannel >= 0) cmp_establish_pp_connection(fdtv, fdtv->subunit, diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c index 9d308dd32a5..5742fde79d9 100644 --- a/drivers/media/dvb/firewire/firedtv-dvb.c +++ b/drivers/media/dvb/firewire/firedtv-dvb.c @@ -268,7 +268,7 @@ struct firedtv *fdtv_alloc(struct device *dev, if (!fdtv) return NULL; - dev->driver_data = fdtv; + dev_set_drvdata(dev, fdtv); fdtv->device = dev; fdtv->isochannel = -1; fdtv->voltage = 0xff; diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c index ba3709bec3f..ec2f45dde16 100644 --- a/drivers/media/video/dabusb.c +++ b/drivers/media/video/dabusb.c @@ -747,8 +747,14 @@ static const struct file_operations dabusb_fops = .release = dabusb_release, }; +static char *dabusb_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + static struct usb_class_driver dabusb_class = { .name = "dabusb%d", + .nodename = dabusb_nodename, .fops = &dabusb_fops, .minor_base = DABUSB_MINOR, }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 299c1cbc383..6c23456e0bd 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -539,7 +539,7 @@ static void class_dev_destroy(struct pvr2_sysfs *sfp) &sfp->attr_unit_number); } pvr2_sysfs_trace("Destroying class_dev id=%p",sfp->class_dev); - sfp->class_dev->driver_data = NULL; + dev_set_drvdata(sfp->class_dev, NULL); device_unregister(sfp->class_dev); sfp->class_dev = NULL; } @@ -549,7 +549,7 @@ static ssize_t v4l_minor_number_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%d\n", pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, @@ -561,7 +561,7 @@ static ssize_t bus_info_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%s\n", pvr2_hdw_get_bus_info(sfp->channel.hdw)); @@ -572,7 +572,7 @@ static ssize_t hdw_name_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%s\n", pvr2_hdw_get_type(sfp->channel.hdw)); @@ -583,7 +583,7 @@ static ssize_t hdw_desc_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%s\n", pvr2_hdw_get_desc(sfp->channel.hdw)); @@ -595,7 +595,7 @@ static ssize_t v4l_radio_minor_number_show(struct device *class_dev, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%d\n", pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, @@ -607,7 +607,7 @@ static ssize_t unit_number_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%d\n", pvr2_hdw_get_unit_number(sfp->channel.hdw)); @@ -635,7 +635,7 @@ static void class_dev_create(struct pvr2_sysfs *sfp, class_dev->parent = &usb_dev->dev; sfp->class_dev = class_dev; - class_dev->driver_data = sfp; + dev_set_drvdata(class_dev, sfp); ret = device_register(class_dev); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, @@ -792,7 +792,7 @@ static ssize_t debuginfo_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; pvr2_hdw_trigger_module_log(sfp->channel.hdw); return pvr2_debugifc_print_info(sfp->channel.hdw,buf,PAGE_SIZE); @@ -803,7 +803,7 @@ static ssize_t debugcmd_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return pvr2_debugifc_print_status(sfp->channel.hdw,buf,PAGE_SIZE); } @@ -816,7 +816,7 @@ static ssize_t debugcmd_store(struct device *class_dev, struct pvr2_sysfs *sfp; int ret; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; ret = pvr2_debugifc_docmd(sfp->channel.hdw,buf,count); diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c index 386da1566fc..cb73051e43d 100644 --- a/drivers/mfd/htc-pasic3.c +++ b/drivers/mfd/htc-pasic3.c @@ -35,7 +35,7 @@ struct pasic3_data { */ void pasic3_write_register(struct device *dev, u32 reg, u8 val) { - struct pasic3_data *asic = dev->driver_data; + struct pasic3_data *asic = dev_get_drvdata(dev); int bus_shift = asic->bus_shift; void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift); void __iomem *data = asic->mapping + (REG_DATA << bus_shift); @@ -50,7 +50,7 @@ EXPORT_SYMBOL(pasic3_write_register); /* for leds-pasic3 */ */ u8 pasic3_read_register(struct device *dev, u32 reg) { - struct pasic3_data *asic = dev->driver_data; + struct pasic3_data *asic = dev_get_drvdata(dev); int bus_shift = asic->bus_shift; void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift); void __iomem *data = asic->mapping + (REG_DATA << bus_shift); diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 11a6248cc1c..082c197ab9b 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -618,7 +618,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client, pdev->dev.parent = pcf->dev; pdev->dev.platform_data = &pdata->reg_init_data[i]; - pdev->dev.driver_data = pcf; + dev_set_drvdata(&pdev->dev, pcf); pcf->regulator_pdev[i] = pdev; platform_device_add(pdev); diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index cf30d06a010..7c21bf79156 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -265,7 +265,7 @@ static int wm8400_init(struct wm8400 *wm8400, mutex_init(&wm8400->io_lock); - wm8400->dev->driver_data = wm8400; + dev_set_drvdata(wm8400->dev, wm8400); /* Check that this is actually a WM8400 */ ret = wm8400->read_dev(wm8400->io_data, WM8400_RESET_ID, 1, ®); diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 89fec052f3b..9118613af32 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -48,6 +48,20 @@ config EEPROM_LEGACY This driver can also be built as a module. If so, the module will be called eeprom. +config EEPROM_MAX6875 + tristate "Maxim MAX6874/5 power supply supervisor" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get read-only support for the user EEPROM of + the Maxim MAX6874/5 EEPROM-programmable, quad power-supply + sequencer/supervisor. + + All other features of this chip should be accessed via i2c-dev. + + This driver can also be built as a module. If so, the module + will be called max6875. + + config EEPROM_93CX6 tristate "EEPROM 93CX6 support" help diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index 539dd8f8812..df3d68ffa9d 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_EEPROM_AT24) += at24.o obj-$(CONFIG_EEPROM_AT25) += at25.o obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o +obj-$(CONFIG_EEPROM_MAX6875) += max6875.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o diff --git a/drivers/i2c/chips/max6875.c b/drivers/misc/eeprom/max6875.c index 033d9d81ec8..3c0c58eed34 100644 --- a/drivers/i2c/chips/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -3,7 +3,7 @@ Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> - Based on i2c/chips/eeprom.c + Based on eeprom.c The MAX6875 has a bank of registers and two banks of EEPROM. Address ranges are defined as follows: diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 01f282cd098..3b6383168c6 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2206,7 +2206,7 @@ config SKGE_DEBUG depends on SKGE && DEBUG_FS help This option adds the ability to dump driver state for debugging. - The file debugfs/skge/ethX displays the state of the internal + The file /sys/kernel/debug/skge/ethX displays the state of the internal transmit and receive rings. If unsure, say N. @@ -2232,7 +2232,7 @@ config SKY2_DEBUG depends on SKY2 && DEBUG_FS help This option adds the ability to dump driver state for debugging. - The file debugfs/sky2/ethX displays the state of the internal + The file /sys/kernel/debug/sky2/ethX displays the state of the internal transmit and receive rings. If unsure, say N. diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c index 30900b30d53..2b38f39924a 100644 --- a/drivers/net/ps3_gelic_net.c +++ b/drivers/net/ps3_gelic_net.c @@ -1648,7 +1648,7 @@ static int ps3_gelic_driver_probe(struct ps3_system_bus_device *dev) result = -ENOMEM; goto fail_alloc_card; } - ps3_system_bus_set_driver_data(dev, card); + ps3_system_bus_set_drvdata(dev, card); card->dev = dev; /* get internal vlan info */ @@ -1749,7 +1749,7 @@ fail_alloc_irq: bus_id(card), 0, 0); fail_status_indicator: - ps3_system_bus_set_driver_data(dev, NULL); + ps3_system_bus_set_drvdata(dev, NULL); kfree(netdev_card(netdev)->unalign); free_netdev(netdev); fail_alloc_card: @@ -1766,7 +1766,7 @@ fail_open: static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev) { - struct gelic_card *card = ps3_system_bus_get_driver_data(dev); + struct gelic_card *card = ps3_system_bus_get_drvdata(dev); struct net_device *netdev0; pr_debug("%s: called\n", __func__); @@ -1803,7 +1803,7 @@ static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev) kfree(netdev_card(netdev0)->unalign); free_netdev(netdev0); - ps3_system_bus_set_driver_data(dev, NULL); + ps3_system_bus_set_drvdata(dev, NULL); ps3_dma_region_free(dev->d_region); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 811d3517fce..11a0ba47b67 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1366,6 +1366,7 @@ static const struct file_operations tun_fops = { static struct miscdevice tun_miscdev = { .minor = TUN_MINOR, .name = "tun", + .devnode = "net/tun", .fops = &tun_fops, }; diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 1fe5da4cf0a..60330f313f2 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h @@ -432,7 +432,7 @@ struct i2400m { unsigned ready:1; /* all probing steps done */ unsigned rx_reorder:1; /* RX reorder is enabled */ u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */ - /* typed u8 so debugfs/u8 can tweak */ + /* typed u8 so /sys/kernel/debug/u8 can tweak */ enum i2400m_system_state state; wait_queue_head_t state_wq; /* Woken up when on state updates */ diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig index 509b6f94f73..daf0c83527d 100644 --- a/drivers/net/wireless/ath/ath5k/Kconfig +++ b/drivers/net/wireless/ath/ath5k/Kconfig @@ -28,11 +28,10 @@ config ATH5K_DEBUG Say Y, if and you will get debug options for ath5k. To use this, you need to mount debugfs: - mkdir /debug/ - mount -t debugfs debug /debug/ + mount -t debugfs debug /sys/kernel/debug You will get access to files under: - /debug/ath5k/phy0/ + /sys/kernel/debug/ath5k/phy0/ To enable debug, pass the debug level to the debug module parameter. For example: diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README index d860fc37575..ab6a2d518af 100644 --- a/drivers/net/wireless/libertas/README +++ b/drivers/net/wireless/libertas/README @@ -72,7 +72,7 @@ rdrf location that is to be read. This parameter must be specified in hexadecimal (its possible to preceed preceding the number with a "0x"). - Path: /debugfs/libertas_wireless/ethX/registers/ + Path: /sys/kernel/debug/libertas_wireless/ethX/registers/ Usage: echo "0xa123" > rdmac ; cat rdmac @@ -95,7 +95,7 @@ wrrf sleepparams This command is used to set the sleepclock configurations - Path: /debugfs/libertas_wireless/ethX/ + Path: /sys/kernel/debug/libertas_wireless/ethX/ Usage: cat sleepparams: reads the current sleepclock configuration @@ -115,7 +115,7 @@ subscribed_events The subscribed_events directory contains the interface for the subscribed events API. - Path: /debugfs/libertas_wireless/ethX/subscribed_events/ + Path: /sys/kernel/debug/libertas_wireless/ethX/subscribed_events/ Each event is represented by a filename. Each filename consists of the following three fields: @@ -165,7 +165,7 @@ subscribed_events extscan This command is used to do a specific scan. - Path: /debugfs/libertas_wireless/ethX/ + Path: /sys/kernel/debug/libertas_wireless/ethX/ Usage: echo "SSID" > extscan @@ -179,7 +179,7 @@ getscantable Display the current contents of the driver scan table (ie. get the scan results). - Path: /debugfs/libertas_wireless/ethX/ + Path: /sys/kernel/debug/libertas_wireless/ethX/ Usage: cat getscantable @@ -188,7 +188,7 @@ setuserscan Initiate a customized scan and retrieve the results - Path: /debugfs/libertas_wireless/ethX/ + Path: /sys/kernel/debug/libertas_wireless/ethX/ Usage: echo "[ARGS]" > setuserscan diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index f8c2898d82b..06a46d7b3d6 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -43,8 +43,8 @@ struct if_spi_card { struct lbs_private *priv; struct libertas_spi_platform_data *pdata; - char helper_fw_name[FIRMWARE_NAME_MAX]; - char main_fw_name[FIRMWARE_NAME_MAX]; + char helper_fw_name[IF_SPI_FW_NAME_MAX]; + char main_fw_name[IF_SPI_FW_NAME_MAX]; /* The card ID and card revision, as reported by the hardware. */ u16 card_id; @@ -1019,9 +1019,9 @@ static int if_spi_calculate_fw_names(u16 card_id, lbs_pr_err("Unsupported chip_id: 0x%02x\n", card_id); return -EAFNOSUPPORT; } - snprintf(helper_fw, FIRMWARE_NAME_MAX, "libertas/gspi%d_hlp.bin", + snprintf(helper_fw, IF_SPI_FW_NAME_MAX, "libertas/gspi%d_hlp.bin", chip_id_to_device_name[i].name); - snprintf(main_fw, FIRMWARE_NAME_MAX, "libertas/gspi%d.bin", + snprintf(main_fw, IF_SPI_FW_NAME_MAX, "libertas/gspi%d.bin", chip_id_to_device_name[i].name); return 0; } diff --git a/drivers/net/wireless/libertas/if_spi.h b/drivers/net/wireless/libertas/if_spi.h index 2103869cc5b..f87eec41084 100644 --- a/drivers/net/wireless/libertas/if_spi.h +++ b/drivers/net/wireless/libertas/if_spi.h @@ -22,6 +22,9 @@ #define IF_SPI_CMD_BUF_SIZE 2400 /***************** Firmware *****************/ + +#define IF_SPI_FW_NAME_MAX 30 + struct chip_ident { u16 chip_id; u16 name; diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index d649caebf08..1844c5adf6e 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -61,11 +61,9 @@ static ssize_t if_usb_firmware_set(struct device *dev, { struct lbs_private *priv = to_net_dev(dev)->ml_priv; struct if_usb_card *cardp = priv->card; - char fwname[FIRMWARE_NAME_MAX]; int ret; - sscanf(buf, "%29s", fwname); /* FIRMWARE_NAME_MAX - 1 = 29 */ - ret = if_usb_prog_firmware(cardp, fwname, BOOT_CMD_UPDATE_FW); + ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_FW); if (ret == 0) return count; @@ -88,11 +86,9 @@ static ssize_t if_usb_boot2_set(struct device *dev, { struct lbs_private *priv = to_net_dev(dev)->ml_priv; struct if_usb_card *cardp = priv->card; - char fwname[FIRMWARE_NAME_MAX]; int ret; - sscanf(buf, "%29s", fwname); /* FIRMWARE_NAME_MAX - 1 = 29 */ - ret = if_usb_prog_firmware(cardp, fwname, BOOT_CMD_UPDATE_BOOT2); + ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_BOOT2); if (ret == 0) return count; diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index f6732538790..8d88daeed0c 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1212,7 +1212,7 @@ static int __devinit netfront_probe(struct xenbus_device *dev, } info = netdev_priv(netdev); - dev->dev.driver_data = info; + dev_set_drvdata(&dev->dev, info); err = register_netdev(info->netdev); if (err) { @@ -1233,7 +1233,7 @@ static int __devinit netfront_probe(struct xenbus_device *dev, fail: free_netdev(netdev); - dev->dev.driver_data = NULL; + dev_set_drvdata(&dev->dev, NULL); return err; } @@ -1275,7 +1275,7 @@ static void xennet_disconnect_backend(struct netfront_info *info) */ static int netfront_resume(struct xenbus_device *dev) { - struct netfront_info *info = dev->dev.driver_data; + struct netfront_info *info = dev_get_drvdata(&dev->dev); dev_dbg(&dev->dev, "%s\n", dev->nodename); @@ -1600,7 +1600,7 @@ static int xennet_connect(struct net_device *dev) static void backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { - struct netfront_info *np = dev->dev.driver_data; + struct netfront_info *np = dev_get_drvdata(&dev->dev); struct net_device *netdev = np->netdev; dev_dbg(&dev->dev, "%s\n", xenbus_strstate(backend_state)); @@ -1774,7 +1774,7 @@ static struct xenbus_device_id netfront_ids[] = { static int __devexit xennet_remove(struct xenbus_device *dev) { - struct netfront_info *info = dev->dev.driver_data; + struct netfront_info *info = dev_get_drvdata(&dev->dev); dev_dbg(&dev->dev, "%s\n", dev->nodename); diff --git a/drivers/parisc/eisa.c b/drivers/parisc/eisa.c index f415fdd9a88..5b89f404e66 100644 --- a/drivers/parisc/eisa.c +++ b/drivers/parisc/eisa.c @@ -373,7 +373,7 @@ static int __init eisa_probe(struct parisc_device *dev) if (result >= 0) { /* FIXME : Don't enumerate the bus twice. */ eisa_dev.root.dev = &dev->dev; - dev->dev.driver_data = &eisa_dev.root; + dev_set_drvdata(&dev->dev, &eisa_dev.root); eisa_dev.root.bus_base_addr = 0; eisa_dev.root.res = &eisa_dev.hba.io_space; eisa_dev.root.slots = result; diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index e5999c4cedc..d46dd57450a 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -2010,7 +2010,7 @@ void __init sba_init(void) void * sba_get_iommu(struct parisc_device *pci_hba) { struct parisc_device *sba_dev = parisc_parent(pci_hba); - struct sba_device *sba = sba_dev->dev.driver_data; + struct sba_device *sba = dev_get_drvdata(&sba_dev->dev); char t = sba_dev->id.hw_type; int iocnum = (pci_hba->hw_path >> 3); /* rope # */ @@ -2031,7 +2031,7 @@ void * sba_get_iommu(struct parisc_device *pci_hba) void sba_directed_lmmio(struct parisc_device *pci_hba, struct resource *r) { struct parisc_device *sba_dev = parisc_parent(pci_hba); - struct sba_device *sba = sba_dev->dev.driver_data; + struct sba_device *sba = dev_get_drvdata(&sba_dev->dev); char t = sba_dev->id.hw_type; int i; int rope = (pci_hba->hw_path & (ROPES_PER_IOC-1)); /* rope # */ @@ -2073,7 +2073,7 @@ void sba_directed_lmmio(struct parisc_device *pci_hba, struct resource *r) void sba_distributed_lmmio(struct parisc_device *pci_hba, struct resource *r ) { struct parisc_device *sba_dev = parisc_parent(pci_hba); - struct sba_device *sba = sba_dev->dev.driver_data; + struct sba_device *sba = dev_get_drvdata(&sba_dev->dev); char t = sba_dev->id.hw_type; int base, size; int rope = (pci_hba->hw_path & (ROPES_PER_IOC-1)); /* rope # */ diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c index ea31a452b15..5d6de380e42 100644 --- a/drivers/parport/parport_gsc.c +++ b/drivers/parport/parport_gsc.c @@ -376,14 +376,14 @@ static int __devinit parport_init_chip(struct parisc_device *dev) /* PARPORT_IRQ_NONE */ PARPORT_DMA_NONE, NULL); if (p) parport_count++; - dev->dev.driver_data = p; + dev_set_drvdata(&dev->dev, p); return 0; } static int __devexit parport_remove_chip(struct parisc_device *dev) { - struct parport *p = dev->dev.driver_data; + struct parport *p = dev_get_drvdata(&dev->dev); if (p) { struct parport_gsc_private *priv = p->private_data; struct parport_operations *ops = p->ops; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index e3998250386..13ffdc35ea0 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -275,7 +275,7 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, memset(device, 0, sizeof(struct device)); device->bus = &pcie_port_bus_type; device->driver = NULL; - device->driver_data = NULL; + dev_set_drvdata(device, NULL); device->release = release_pcie_device; /* callback to free pcie dev */ dev_set_name(device, "%s:pcie%02x", pci_name(parent), get_descriptor_id(port_type, service_type)); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 47cab31ff6e..304ff6d5cf3 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -394,7 +394,7 @@ static int pcmcia_device_probe(struct device * dev) p_drv = to_pcmcia_drv(dev->driver); s = p_dev->socket; - /* The PCMCIA code passes the match data in via dev->driver_data + /* The PCMCIA code passes the match data in via dev_set_drvdata(dev) * which is an ugly hack. Once the driver probe is called it may * and often will overwrite the match data so we must save it first * @@ -404,7 +404,7 @@ static int pcmcia_device_probe(struct device * dev) * call which will then check whether there are two * pseudo devices, and if not, add the second one. */ - did = p_dev->dev.driver_data; + did = dev_get_drvdata(&p_dev->dev); ds_dev_dbg(1, dev, "trying to bind to %s\n", p_drv->drv.name); @@ -499,7 +499,7 @@ static int pcmcia_device_remove(struct device * dev) * pseudo multi-function card, we need to unbind * all devices */ - did = p_dev->dev.driver_data; + did = dev_get_drvdata(&p_dev->dev); if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && (p_dev->socket->device_count != 0) && (p_dev->device_no == 0)) @@ -828,7 +828,6 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) { struct pcmcia_socket *s = dev->socket; const struct firmware *fw; - char path[FIRMWARE_NAME_MAX]; int ret = -ENOMEM; int no_funcs; int old_funcs; @@ -839,16 +838,7 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) ds_dev_dbg(1, &dev->dev, "trying to load CIS file %s\n", filename); - if (strlen(filename) > (FIRMWARE_NAME_MAX - 1)) { - dev_printk(KERN_WARNING, &dev->dev, - "pcmcia: CIS filename is too long [%s]\n", - filename); - return -EINVAL; - } - - snprintf(path, sizeof(path), "%s", filename); - - if (request_firmware(&fw, path, &dev->dev) == 0) { + if (request_firmware(&fw, filename, &dev->dev) == 0) { if (fw->size >= CISTPL_MAX_CIS_SIZE) { ret = -EINVAL; dev_printk(KERN_ERR, &dev->dev, @@ -988,7 +978,7 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, return 0; } - dev->dev.driver_data = (void *) did; + dev_set_drvdata(&dev->dev, did); return 1; } diff --git a/drivers/ps3/ps3-sys-manager.c b/drivers/ps3/ps3-sys-manager.c index f17513dd9d4..88cb7408861 100644 --- a/drivers/ps3/ps3-sys-manager.c +++ b/drivers/ps3/ps3-sys-manager.c @@ -706,7 +706,7 @@ static void ps3_sys_manager_work(struct ps3_system_bus_device *dev) ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN); } -static int ps3_sys_manager_probe(struct ps3_system_bus_device *dev) +static int __devinit ps3_sys_manager_probe(struct ps3_system_bus_device *dev) { int result; struct ps3_sys_manager_ops ops; diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c index 235e87fcb49..e82d8c9c6cd 100644 --- a/drivers/ps3/ps3av.c +++ b/drivers/ps3/ps3av.c @@ -80,12 +80,12 @@ static const struct avset_video_mode { { 0, }, /* auto */ {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I, A_N, 720, 480}, {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P, A_N, 720, 480}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_N, 1280, 720}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_W, 1280, 720}, {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080}, {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080}, {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I, A_N, 720, 576}, {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P, A_N, 720, 576}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_N, 1280, 720}, + {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_W, 1280, 720}, {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080}, {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080}, { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA, A_W, 1280, 768}, @@ -937,7 +937,7 @@ int ps3av_audio_mute(int mute) EXPORT_SYMBOL_GPL(ps3av_audio_mute); -static int ps3av_probe(struct ps3_system_bus_device *dev) +static int __devinit ps3av_probe(struct ps3_system_bus_device *dev) { int res; int id; @@ -1048,7 +1048,7 @@ static struct ps3_vuart_port_driver ps3av_driver = { .shutdown = ps3av_shutdown, }; -static int ps3av_module_init(void) +static int __init ps3av_module_init(void) { int error; diff --git a/drivers/ps3/ps3av_cmd.c b/drivers/ps3/ps3av_cmd.c index 716596e8e5b..f555fedd507 100644 --- a/drivers/ps3/ps3av_cmd.c +++ b/drivers/ps3/ps3av_cmd.c @@ -21,9 +21,10 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/delay.h> + #include <asm/ps3av.h> -#include <asm/ps3fb.h> #include <asm/ps3.h> +#include <asm/ps3gpu.h> #include "vuart.h" diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 442bb98a282..e5b84db0aa0 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -5,8 +5,7 @@ * Carsten Otte <Cotte@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 - * + * Copyright IBM Corp. 1999, 2009 */ #define KMSG_COMPONENT "dasd" @@ -61,6 +60,7 @@ static int dasd_flush_block_queue(struct dasd_block *); static void dasd_device_tasklet(struct dasd_device *); static void dasd_block_tasklet(struct dasd_block *); static void do_kick_device(struct work_struct *); +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); @@ -109,6 +109,7 @@ struct dasd_device *dasd_alloc_device(void) device->timer.function = dasd_device_timeout; device->timer.data = (unsigned long) device; INIT_WORK(&device->kick_work, do_kick_device); + INIT_WORK(&device->restore_device, do_restore_device); device->state = DASD_STATE_NEW; device->target = DASD_STATE_NEW; @@ -512,6 +513,25 @@ void dasd_kick_device(struct dasd_device *device) } /* + * dasd_restore_device will schedule a call do do_restore_device to the kernel + * event daemon. + */ +static void do_restore_device(struct work_struct *work) +{ + struct dasd_device *device = container_of(work, struct dasd_device, + restore_device); + device->cdev->drv->restore(device->cdev); + dasd_put_device(device); +} + +void dasd_restore_device(struct dasd_device *device) +{ + dasd_get_device(device); + /* queue call to dasd_restore_device to the kernel event daemon. */ + schedule_work(&device->restore_device); +} + +/* * Set the target state for a device and starts the state change. */ void dasd_set_target_state(struct dasd_device *device, int target) @@ -908,6 +928,12 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) DBF_DEV_EVENT(DBF_DEBUG, device, "%s", "start_IO: -EIO device gone, retry"); break; + case -EINVAL: + /* most likely caused in power management context */ + DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + "start_IO: -EINVAL device currently " + "not accessible"); + break; default: /* internal error 11 - unknown rc */ snprintf(errorstring, ERRORLENGTH, "11 %d", rc); @@ -2400,6 +2426,12 @@ int dasd_generic_notify(struct ccw_device *cdev, int event) case CIO_OPER: /* FIXME: add a sanity check. */ device->stopped &= ~DASD_STOPPED_DC_WAIT; + if (device->stopped & DASD_UNRESUMED_PM) { + device->stopped &= ~DASD_UNRESUMED_PM; + dasd_restore_device(device); + ret = 1; + break; + } dasd_schedule_device_bh(device); if (device->block) dasd_schedule_block_bh(device->block); @@ -2410,6 +2442,79 @@ int dasd_generic_notify(struct ccw_device *cdev, int event) return ret; } +int dasd_generic_pm_freeze(struct ccw_device *cdev) +{ + struct dasd_ccw_req *cqr, *n; + int rc; + struct list_head freeze_queue; + struct dasd_device *device = dasd_device_from_cdev(cdev); + + if (IS_ERR(device)) + return PTR_ERR(device); + /* disallow new I/O */ + device->stopped |= DASD_STOPPED_PM; + /* clear active requests */ + INIT_LIST_HEAD(&freeze_queue); + spin_lock_irq(get_ccwdev_lock(cdev)); + rc = 0; + list_for_each_entry_safe(cqr, n, &device->ccw_queue, devlist) { + /* Check status and move request to flush_queue */ + if (cqr->status == DASD_CQR_IN_IO) { + rc = device->discipline->term_IO(cqr); + if (rc) { + /* unable to terminate requeust */ + dev_err(&device->cdev->dev, + "Unable to terminate request %p " + "on suspend\n", cqr); + spin_unlock_irq(get_ccwdev_lock(cdev)); + dasd_put_device(device); + return rc; + } + } + list_move_tail(&cqr->devlist, &freeze_queue); + } + + spin_unlock_irq(get_ccwdev_lock(cdev)); + + list_for_each_entry_safe(cqr, n, &freeze_queue, devlist) { + wait_event(dasd_flush_wq, + (cqr->status != DASD_CQR_CLEAR_PENDING)); + if (cqr->status == DASD_CQR_CLEARED) + cqr->status = DASD_CQR_QUEUED; + } + /* move freeze_queue to start of the ccw_queue */ + spin_lock_irq(get_ccwdev_lock(cdev)); + list_splice_tail(&freeze_queue, &device->ccw_queue); + spin_unlock_irq(get_ccwdev_lock(cdev)); + + if (device->discipline->freeze) + rc = device->discipline->freeze(device); + + dasd_put_device(device); + return rc; +} +EXPORT_SYMBOL_GPL(dasd_generic_pm_freeze); + +int dasd_generic_restore_device(struct ccw_device *cdev) +{ + struct dasd_device *device = dasd_device_from_cdev(cdev); + int rc = 0; + + if (IS_ERR(device)) + return PTR_ERR(device); + + dasd_schedule_device_bh(device); + if (device->block) + dasd_schedule_block_bh(device->block); + + if (device->discipline->restore) + rc = device->discipline->restore(device); + + dasd_put_device(device); + return rc; +} +EXPORT_SYMBOL_GPL(dasd_generic_restore_device); + static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, void *rdc_buffer, int rdc_buffer_size, diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index e77666c8e6c..4cac5b54f26 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -1098,6 +1098,7 @@ dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid) spin_unlock(&dasd_devmap_lock); return 0; } +EXPORT_SYMBOL_GPL(dasd_get_uid); /* * Register the given device unique identifier into devmap struct. diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index cf0cfdba124..1c28ec3e4cc 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -5,10 +5,9 @@ * Carsten Otte <Cotte@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 + * Copyright IBM Corp. 1999, 2009 * EMC Symmetrix ioctl Copyright EMC Corporation, 2008 * Author.........: Nigel Hislop <hislop_nigel@emc.com> - * */ #define KMSG_COMPONENT "dasd" @@ -104,17 +103,6 @@ dasd_eckd_set_online(struct ccw_device *cdev) return dasd_generic_set_online(cdev, &dasd_eckd_discipline); } -static struct ccw_driver dasd_eckd_driver = { - .name = "dasd-eckd", - .owner = THIS_MODULE, - .ids = dasd_eckd_ids, - .probe = dasd_eckd_probe, - .remove = dasd_generic_remove, - .set_offline = dasd_generic_set_offline, - .set_online = dasd_eckd_set_online, - .notify = dasd_generic_notify, -}; - static const int sizes_trk0[] = { 28, 148, 84 }; #define LABEL_SIZE 140 @@ -3236,6 +3224,98 @@ static void dasd_eckd_dump_sense(struct dasd_device *device, dasd_eckd_dump_sense_ccw(device, req, irb); } +int dasd_eckd_pm_freeze(struct dasd_device *device) +{ + /* + * the device should be disconnected from our LCU structure + * on restore we will reconnect it and reread LCU specific + * information like PAV support that might have changed + */ + dasd_alias_remove_device(device); + dasd_alias_disconnect_device_from_lcu(device); + + return 0; +} + +int dasd_eckd_restore_device(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + int is_known, rc; + struct dasd_uid temp_uid; + + /* allow new IO again */ + device->stopped &= ~DASD_STOPPED_PM; + + private = (struct dasd_eckd_private *) device->private; + + /* Read Configuration Data */ + rc = dasd_eckd_read_conf(device); + if (rc) + goto out_err; + + /* Generate device unique id and register in devmap */ + rc = dasd_eckd_generate_uid(device, &private->uid); + dasd_get_uid(device->cdev, &temp_uid); + if (memcmp(&private->uid, &temp_uid, sizeof(struct dasd_uid)) != 0) + dev_err(&device->cdev->dev, "The UID of the DASD has changed\n"); + if (rc) + goto out_err; + dasd_set_uid(device->cdev, &private->uid); + + /* register lcu with alias handling, enable PAV if this is a new lcu */ + is_known = dasd_alias_make_device_known_to_lcu(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; + } + + /* Read Feature Codes */ + rc = dasd_eckd_read_features(device); + if (rc) + goto out_err; + + /* Read Device Characteristics */ + memset(&private->rdc_data, 0, sizeof(private->rdc_data)); + rc = dasd_generic_read_dev_chars(device, "ECKD", + &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)); + goto out_err; + } + + /* add device to alias management */ + dasd_alias_add_device(device); + + return 0; + +out_err: + /* + * if the resume failed for the DASD we put it in + * an UNRESUMED stop state + */ + device->stopped |= DASD_UNRESUMED_PM; + return 0; +} + +static struct ccw_driver dasd_eckd_driver = { + .name = "dasd-eckd", + .owner = THIS_MODULE, + .ids = dasd_eckd_ids, + .probe = dasd_eckd_probe, + .remove = dasd_generic_remove, + .set_offline = dasd_generic_set_offline, + .set_online = dasd_eckd_set_online, + .notify = dasd_generic_notify, + .freeze = dasd_generic_pm_freeze, + .thaw = dasd_generic_restore_device, + .restore = dasd_generic_restore_device, +}; /* * max_blocks is dependent on the amount of storage that is available @@ -3274,6 +3354,8 @@ static struct dasd_discipline dasd_eckd_discipline = { .dump_sense_dbf = dasd_eckd_dump_sense_dbf, .fill_info = dasd_eckd_fill_info, .ioctl = dasd_eckd_ioctl, + .freeze = dasd_eckd_pm_freeze, + .restore = dasd_eckd_restore_device, }; static int __init diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 597c6ffdb9f..e21ee735f92 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -2,8 +2,7 @@ * File...........: linux/drivers/s390/block/dasd_fba.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 - * + * Copyright IBM Corp. 1999, 2009 */ #define KMSG_COMPONENT "dasd" @@ -75,6 +74,9 @@ static struct ccw_driver dasd_fba_driver = { .set_offline = dasd_generic_set_offline, .set_online = dasd_fba_set_online, .notify = dasd_generic_notify, + .freeze = dasd_generic_pm_freeze, + .thaw = dasd_generic_restore_device, + .restore = dasd_generic_restore_device, }; static void diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index f97ceb79507..fd63b2f2bda 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -4,8 +4,7 @@ * Horst Hummel <Horst.Hummel@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 - * + * Copyright IBM Corp. 1999, 2009 */ #ifndef DASD_INT_H @@ -295,6 +294,10 @@ struct dasd_discipline { int (*fill_geometry) (struct dasd_block *, struct hd_geometry *); int (*fill_info) (struct dasd_device *, struct dasd_information2_t *); int (*ioctl) (struct dasd_block *, unsigned int, void __user *); + + /* suspend/resume functions */ + int (*freeze) (struct dasd_device *); + int (*restore) (struct dasd_device *); }; extern struct dasd_discipline *dasd_diag_discipline_pointer; @@ -367,6 +370,7 @@ struct dasd_device { atomic_t tasklet_scheduled; struct tasklet_struct tasklet; struct work_struct kick_work; + struct work_struct restore_device; struct timer_list timer; debug_info_t *debug_area; @@ -410,6 +414,8 @@ struct dasd_block { #define DASD_STOPPED_PENDING 4 /* long busy */ #define DASD_STOPPED_DC_WAIT 8 /* disconnected, wait */ #define DASD_STOPPED_SU 16 /* summary unit check handling */ +#define DASD_STOPPED_PM 32 /* pm state transition */ +#define DASD_UNRESUMED_PM 64 /* pm resume failed state */ /* per device flags */ #define DASD_FLAG_OFFLINE 3 /* device is in offline processing */ @@ -556,6 +562,7 @@ void dasd_free_block(struct dasd_block *); void dasd_enable_device(struct dasd_device *); void dasd_set_target_state(struct dasd_device *, int); void dasd_kick_device(struct dasd_device *); +void dasd_restore_device(struct dasd_device *); void dasd_add_request_head(struct dasd_ccw_req *); void dasd_add_request_tail(struct dasd_ccw_req *); @@ -578,6 +585,8 @@ int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *); int dasd_generic_set_offline (struct ccw_device *cdev); int dasd_generic_notify(struct ccw_device *, int); void dasd_generic_handle_state_change(struct dasd_device *); +int dasd_generic_pm_freeze(struct ccw_device *); +int dasd_generic_restore_device(struct ccw_device *); int dasd_generic_read_dev_chars(struct dasd_device *, char *, void *, int); char *dasd_get_sense(struct irb *); diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index b21caf177e3..016f9e9d259 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -14,10 +14,11 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/blkdev.h> -#include <asm/extmem.h> -#include <asm/io.h> #include <linux/completion.h> #include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <asm/extmem.h> +#include <asm/io.h> #define DCSSBLK_NAME "dcssblk" #define DCSSBLK_MINORS_PER_DISK 1 @@ -940,11 +941,94 @@ dcssblk_check_params(void) } /* + * Suspend / Resume + */ +static int dcssblk_freeze(struct device *dev) +{ + struct dcssblk_dev_info *dev_info; + int rc = 0; + + list_for_each_entry(dev_info, &dcssblk_devices, lh) { + switch (dev_info->segment_type) { + case SEG_TYPE_SR: + case SEG_TYPE_ER: + case SEG_TYPE_SC: + if (!dev_info->is_shared) + rc = -EINVAL; + break; + default: + rc = -EINVAL; + break; + } + if (rc) + break; + } + if (rc) + pr_err("Suspend failed because device %s is writeable.\n", + dev_info->segment_name); + return rc; +} + +static int dcssblk_restore(struct device *dev) +{ + struct dcssblk_dev_info *dev_info; + struct segment_info *entry; + unsigned long start, end; + int rc = 0; + + list_for_each_entry(dev_info, &dcssblk_devices, lh) { + list_for_each_entry(entry, &dev_info->seg_list, lh) { + segment_unload(entry->segment_name); + rc = segment_load(entry->segment_name, SEGMENT_SHARED, + &start, &end); + if (rc < 0) { +// TODO in_use check ? + segment_warning(rc, entry->segment_name); + goto out_panic; + } + if (start != entry->start || end != entry->end) { + pr_err("Mismatch of start / end address after " + "resuming device %s\n", + entry->segment_name); + goto out_panic; + } + } + } + return 0; +out_panic: + panic("fatal dcssblk resume error\n"); +} + +static int dcssblk_thaw(struct device *dev) +{ + return 0; +} + +static struct dev_pm_ops dcssblk_pm_ops = { + .freeze = dcssblk_freeze, + .thaw = dcssblk_thaw, + .restore = dcssblk_restore, +}; + +static struct platform_driver dcssblk_pdrv = { + .driver = { + .name = "dcssblk", + .owner = THIS_MODULE, + .pm = &dcssblk_pm_ops, + }, +}; + +static struct platform_device *dcssblk_pdev; + + +/* * The init/exit functions. */ static void __exit dcssblk_exit(void) { + platform_device_unregister(dcssblk_pdev); + platform_driver_unregister(&dcssblk_pdrv); root_device_unregister(dcssblk_root_dev); unregister_blkdev(dcssblk_major, DCSSBLK_NAME); } @@ -954,30 +1038,44 @@ dcssblk_init(void) { int rc; - dcssblk_root_dev = root_device_register("dcssblk"); - if (IS_ERR(dcssblk_root_dev)) - return PTR_ERR(dcssblk_root_dev); - rc = device_create_file(dcssblk_root_dev, &dev_attr_add); - if (rc) { - root_device_unregister(dcssblk_root_dev); + rc = platform_driver_register(&dcssblk_pdrv); + if (rc) return rc; + + dcssblk_pdev = platform_device_register_simple("dcssblk", -1, NULL, + 0); + if (IS_ERR(dcssblk_pdev)) { + rc = PTR_ERR(dcssblk_pdev); + goto out_pdrv; } - rc = device_create_file(dcssblk_root_dev, &dev_attr_remove); - if (rc) { - root_device_unregister(dcssblk_root_dev); - return rc; + + dcssblk_root_dev = root_device_register("dcssblk"); + if (IS_ERR(dcssblk_root_dev)) { + rc = PTR_ERR(dcssblk_root_dev); + goto out_pdev; } + rc = device_create_file(dcssblk_root_dev, &dev_attr_add); + if (rc) + goto out_root; + rc = device_create_file(dcssblk_root_dev, &dev_attr_remove); + if (rc) + goto out_root; rc = register_blkdev(0, DCSSBLK_NAME); - if (rc < 0) { - root_device_unregister(dcssblk_root_dev); - return rc; - } + if (rc < 0) + goto out_root; dcssblk_major = rc; init_rwsem(&dcssblk_devices_sem); dcssblk_check_params(); - return 0; + +out_root: + root_device_unregister(dcssblk_root_dev); +out_pdev: + platform_device_unregister(dcssblk_pdev); +out_pdrv: + platform_driver_unregister(&dcssblk_pdrv); + return rc; } module_init(dcssblk_init); diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index 0ae0c83ef87..2e9e1ecd6d8 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -39,7 +39,10 @@ #include <linux/hdreg.h> /* HDIO_GETGEO */ #include <linux/sysdev.h> #include <linux/bio.h> +#include <linux/suspend.h> +#include <linux/platform_device.h> #include <asm/uaccess.h> +#include <asm/checksum.h> #define XPRAM_NAME "xpram" #define XPRAM_DEVS 1 /* one partition */ @@ -48,6 +51,7 @@ typedef struct { unsigned int size; /* size of xpram segment in pages */ unsigned int offset; /* start page of xpram segment */ + unsigned int csum; /* partition checksum for suspend */ } xpram_device_t; static xpram_device_t xpram_devices[XPRAM_MAX_DEVS]; @@ -138,7 +142,7 @@ static long xpram_page_out (unsigned long page_addr, unsigned int xpage_index) /* * Check if xpram is available. */ -static int __init xpram_present(void) +static int xpram_present(void) { unsigned long mem_page; int rc; @@ -154,7 +158,7 @@ static int __init xpram_present(void) /* * Return index of the last available xpram page. */ -static unsigned long __init xpram_highest_page_index(void) +static unsigned long xpram_highest_page_index(void) { unsigned int page_index, add_bit; unsigned long mem_page; @@ -383,6 +387,106 @@ out: } /* + * Save checksums for all partitions. + */ +static int xpram_save_checksums(void) +{ + unsigned long mem_page; + int rc, i; + + rc = 0; + mem_page = (unsigned long) __get_free_page(GFP_KERNEL); + if (!mem_page) + return -ENOMEM; + for (i = 0; i < xpram_devs; i++) { + rc = xpram_page_in(mem_page, xpram_devices[i].offset); + if (rc) + goto fail; + xpram_devices[i].csum = csum_partial((const void *) mem_page, + PAGE_SIZE, 0); + } +fail: + free_page(mem_page); + return rc ? -ENXIO : 0; +} + +/* + * Verify checksums for all partitions. + */ +static int xpram_validate_checksums(void) +{ + unsigned long mem_page; + unsigned int csum; + int rc, i; + + rc = 0; + mem_page = (unsigned long) __get_free_page(GFP_KERNEL); + if (!mem_page) + return -ENOMEM; + for (i = 0; i < xpram_devs; i++) { + rc = xpram_page_in(mem_page, xpram_devices[i].offset); + if (rc) + goto fail; + csum = csum_partial((const void *) mem_page, PAGE_SIZE, 0); + if (xpram_devices[i].csum != csum) { + rc = -EINVAL; + goto fail; + } + } +fail: + free_page(mem_page); + return rc ? -ENXIO : 0; +} + +/* + * Resume failed: Print error message and call panic. + */ +static void xpram_resume_error(const char *message) +{ + pr_err("Resume error: %s\n", message); + panic("xpram resume error\n"); +} + +/* + * Check if xpram setup changed between suspend and resume. + */ +static int xpram_restore(struct device *dev) +{ + if (!xpram_pages) + return 0; + if (xpram_present() != 0) + xpram_resume_error("xpram disappeared"); + if (xpram_pages != xpram_highest_page_index() + 1) + xpram_resume_error("Size of xpram changed"); + if (xpram_validate_checksums()) + xpram_resume_error("Data of xpram changed"); + return 0; +} + +/* + * Save necessary state in suspend. + */ +static int xpram_freeze(struct device *dev) +{ + return xpram_save_checksums(); +} + +static struct dev_pm_ops xpram_pm_ops = { + .freeze = xpram_freeze, + .restore = xpram_restore, +}; + +static struct platform_driver xpram_pdrv = { + .driver = { + .name = XPRAM_NAME, + .owner = THIS_MODULE, + .pm = &xpram_pm_ops, + }, +}; + +static struct platform_device *xpram_pdev; + +/* * Finally, the init/exit functions. */ static void __exit xpram_exit(void) @@ -394,6 +498,8 @@ static void __exit xpram_exit(void) put_disk(xpram_disks[i]); } unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME); + platform_device_unregister(xpram_pdev); + platform_driver_unregister(&xpram_pdrv); } static int __init xpram_init(void) @@ -411,7 +517,24 @@ static int __init xpram_init(void) rc = xpram_setup_sizes(xpram_pages); if (rc) return rc; - return xpram_setup_blkdev(); + rc = platform_driver_register(&xpram_pdrv); + if (rc) + return rc; + xpram_pdev = platform_device_register_simple(XPRAM_NAME, -1, NULL, 0); + if (IS_ERR(xpram_pdev)) { + rc = PTR_ERR(xpram_pdev); + goto fail_platform_driver_unregister; + } + rc = xpram_setup_blkdev(); + if (rc) + goto fail_platform_device_unregister; + return 0; + +fail_platform_device_unregister: + platform_device_unregister(xpram_pdev); +fail_platform_driver_unregister: + platform_driver_unregister(&xpram_pdrv); + return rc; } module_init(xpram_init); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 9ab06e0dad4..04dc734805c 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -1,14 +1,12 @@ /* - * drivers/s390/char/con3215.c - * 3215 line mode terminal driver. + * 3215 line mode terminal driver. * - * S390 version - * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Copyright IBM Corp. 1999, 2009 + * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> * - * Updated: - * Aug-2000: Added tab support - * Dan Morrison, IBM Corporation (dmorriso@cse.buffalo.edu) + * Updated: + * Aug-2000: Added tab support + * Dan Morrison, IBM Corporation <dmorriso@cse.buffalo.edu> */ #include <linux/module.h> @@ -56,6 +54,7 @@ #define RAW3215_CLOSING 32 /* set while in close process */ #define RAW3215_TIMER_RUNS 64 /* set if the output delay timer is on */ #define RAW3215_FLUSHING 128 /* set to flush buffer (no delay) */ +#define RAW3215_FROZEN 256 /* set if 3215 is frozen for suspend */ #define TAB_STOP_SIZE 8 /* tab stop size */ @@ -111,8 +110,8 @@ static struct tty_driver *tty3215_driver; /* * Get a request structure from the free list */ -static inline struct raw3215_req * -raw3215_alloc_req(void) { +static inline struct raw3215_req *raw3215_alloc_req(void) +{ struct raw3215_req *req; unsigned long flags; @@ -126,8 +125,8 @@ raw3215_alloc_req(void) { /* * Put a request structure back to the free list */ -static inline void -raw3215_free_req(struct raw3215_req *req) { +static inline void raw3215_free_req(struct raw3215_req *req) +{ unsigned long flags; if (req->type == RAW3215_FREE) @@ -145,8 +144,7 @@ raw3215_free_req(struct raw3215_req *req) { * because a 3215 terminal won't accept a new read before the old one is * completed. */ -static void -raw3215_mk_read_req(struct raw3215_info *raw) +static void raw3215_mk_read_req(struct raw3215_info *raw) { struct raw3215_req *req; struct ccw1 *ccw; @@ -174,8 +172,7 @@ raw3215_mk_read_req(struct raw3215_info *raw) * buffer to the 3215 device. If a queued write exists it is replaced by * the new, probably lengthened request. */ -static void -raw3215_mk_write_req(struct raw3215_info *raw) +static void raw3215_mk_write_req(struct raw3215_info *raw) { struct raw3215_req *req; struct ccw1 *ccw; @@ -251,8 +248,7 @@ raw3215_mk_write_req(struct raw3215_info *raw) /* * Start a read or a write request */ -static void -raw3215_start_io(struct raw3215_info *raw) +static void raw3215_start_io(struct raw3215_info *raw) { struct raw3215_req *req; int res; @@ -290,8 +286,7 @@ raw3215_start_io(struct raw3215_info *raw) /* * Function to start a delayed output after RAW3215_TIMEOUT seconds */ -static void -raw3215_timeout(unsigned long __data) +static void raw3215_timeout(unsigned long __data) { struct raw3215_info *raw = (struct raw3215_info *) __data; unsigned long flags; @@ -300,8 +295,10 @@ raw3215_timeout(unsigned long __data) if (raw->flags & RAW3215_TIMER_RUNS) { del_timer(&raw->timer); raw->flags &= ~RAW3215_TIMER_RUNS; - raw3215_mk_write_req(raw); - raw3215_start_io(raw); + if (!(raw->flags & RAW3215_FROZEN)) { + raw3215_mk_write_req(raw); + raw3215_start_io(raw); + } } spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); } @@ -312,10 +309,9 @@ raw3215_timeout(unsigned long __data) * amount of data is bigger than RAW3215_MIN_WRITE. If a write is not * done immediately a timer is started with a delay of RAW3215_TIMEOUT. */ -static inline void -raw3215_try_io(struct raw3215_info *raw) +static inline void raw3215_try_io(struct raw3215_info *raw) { - if (!(raw->flags & RAW3215_ACTIVE)) + if (!(raw->flags & RAW3215_ACTIVE) || (raw->flags & RAW3215_FROZEN)) return; if (raw->queued_read != NULL) raw3215_start_io(raw); @@ -359,8 +355,8 @@ static void raw3215_next_io(struct raw3215_info *raw) /* * Interrupt routine, called from common io layer */ -static void -raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) +static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm, + struct irb *irb) { struct raw3215_info *raw; struct raw3215_req *req; @@ -368,7 +364,7 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) int cstat, dstat; int count; - raw = cdev->dev.driver_data; + raw = dev_get_drvdata(&cdev->dev); req = (struct raw3215_req *) intparm; cstat = irb->scsw.cmd.cstat; dstat = irb->scsw.cmd.dstat; @@ -459,14 +455,40 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) } /* + * Drop the oldest line from the output buffer. + */ +static void raw3215_drop_line(struct raw3215_info *raw) +{ + int ix; + char ch; + + BUG_ON(raw->written != 0); + ix = (raw->head - raw->count) & (RAW3215_BUFFER_SIZE - 1); + while (raw->count > 0) { + ch = raw->buffer[ix]; + ix = (ix + 1) & (RAW3215_BUFFER_SIZE - 1); + raw->count--; + if (ch == 0x15) + break; + } + raw->head = ix; +} + +/* * Wait until length bytes are available int the output buffer. * Has to be called with the s390irq lock held. Can be called * disabled. */ -static void -raw3215_make_room(struct raw3215_info *raw, unsigned int length) +static void raw3215_make_room(struct raw3215_info *raw, unsigned int length) { while (RAW3215_BUFFER_SIZE - raw->count < length) { + /* While console is frozen for suspend we have no other + * choice but to drop message from the buffer to make + * room for even more messages. */ + if (raw->flags & RAW3215_FROZEN) { + raw3215_drop_line(raw); + continue; + } /* there might be a request pending */ raw->flags |= RAW3215_FLUSHING; raw3215_mk_write_req(raw); @@ -488,8 +510,8 @@ raw3215_make_room(struct raw3215_info *raw, unsigned int length) /* * String write routine for 3215 devices */ -static void -raw3215_write(struct raw3215_info *raw, const char *str, unsigned int length) +static void raw3215_write(struct raw3215_info *raw, const char *str, + unsigned int length) { unsigned long flags; int c, count; @@ -529,8 +551,7 @@ raw3215_write(struct raw3215_info *raw, const char *str, unsigned int length) /* * Put character routine for 3215 devices */ -static void -raw3215_putchar(struct raw3215_info *raw, unsigned char ch) +static void raw3215_putchar(struct raw3215_info *raw, unsigned char ch) { unsigned long flags; unsigned int length, i; @@ -566,8 +587,7 @@ raw3215_putchar(struct raw3215_info *raw, unsigned char ch) * Flush routine, it simply sets the flush flag and tries to start * pending IO. */ -static void -raw3215_flush_buffer(struct raw3215_info *raw) +static void raw3215_flush_buffer(struct raw3215_info *raw) { unsigned long flags; @@ -583,8 +603,7 @@ raw3215_flush_buffer(struct raw3215_info *raw) /* * Fire up a 3215 device. */ -static int -raw3215_startup(struct raw3215_info *raw) +static int raw3215_startup(struct raw3215_info *raw) { unsigned long flags; @@ -602,8 +621,7 @@ raw3215_startup(struct raw3215_info *raw) /* * Shutdown a 3215 device. */ -static void -raw3215_shutdown(struct raw3215_info *raw) +static void raw3215_shutdown(struct raw3215_info *raw) { DECLARE_WAITQUEUE(wait, current); unsigned long flags; @@ -628,14 +646,13 @@ raw3215_shutdown(struct raw3215_info *raw) spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); } -static int -raw3215_probe (struct ccw_device *cdev) +static int raw3215_probe (struct ccw_device *cdev) { struct raw3215_info *raw; int line; /* Console is special. */ - if (raw3215[0] && (cdev->dev.driver_data == raw3215[0])) + if (raw3215[0] && (raw3215[0] == dev_get_drvdata(&cdev->dev))) return 0; raw = kmalloc(sizeof(struct raw3215_info) + RAW3215_INBUF_SIZE, GFP_KERNEL|GFP_DMA); @@ -669,44 +686,41 @@ raw3215_probe (struct ccw_device *cdev) } init_waitqueue_head(&raw->empty_wait); - cdev->dev.driver_data = raw; + dev_set_drvdata(&cdev->dev, raw); cdev->handler = raw3215_irq; return 0; } -static void -raw3215_remove (struct ccw_device *cdev) +static void raw3215_remove (struct ccw_device *cdev) { struct raw3215_info *raw; ccw_device_set_offline(cdev); - raw = cdev->dev.driver_data; + raw = dev_get_drvdata(&cdev->dev); if (raw) { - cdev->dev.driver_data = NULL; + dev_set_drvdata(&cdev->dev, NULL); kfree(raw->buffer); kfree(raw); } } -static int -raw3215_set_online (struct ccw_device *cdev) +static int raw3215_set_online (struct ccw_device *cdev) { struct raw3215_info *raw; - raw = cdev->dev.driver_data; + raw = dev_get_drvdata(&cdev->dev); if (!raw) return -ENODEV; return raw3215_startup(raw); } -static int -raw3215_set_offline (struct ccw_device *cdev) +static int raw3215_set_offline (struct ccw_device *cdev) { struct raw3215_info *raw; - raw = cdev->dev.driver_data; + raw = dev_get_drvdata(&cdev->dev); if (!raw) return -ENODEV; @@ -715,6 +729,36 @@ raw3215_set_offline (struct ccw_device *cdev) return 0; } +static int raw3215_pm_stop(struct ccw_device *cdev) +{ + struct raw3215_info *raw; + unsigned long flags; + + /* Empty the output buffer, then prevent new I/O. */ + raw = cdev->dev.driver_data; + spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); + raw3215_make_room(raw, RAW3215_BUFFER_SIZE); + raw->flags |= RAW3215_FROZEN; + spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); + return 0; +} + +static int raw3215_pm_start(struct ccw_device *cdev) +{ + struct raw3215_info *raw; + unsigned long flags; + + /* Allow I/O again and flush output buffer. */ + raw = cdev->dev.driver_data; + spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); + raw->flags &= ~RAW3215_FROZEN; + raw->flags |= RAW3215_FLUSHING; + raw3215_try_io(raw); + raw->flags &= ~RAW3215_FLUSHING; + spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); + return 0; +} + static struct ccw_device_id raw3215_id[] = { { CCW_DEVICE(0x3215, 0) }, { /* end of list */ }, @@ -728,14 +772,17 @@ static struct ccw_driver raw3215_ccw_driver = { .remove = &raw3215_remove, .set_online = &raw3215_set_online, .set_offline = &raw3215_set_offline, + .freeze = &raw3215_pm_stop, + .thaw = &raw3215_pm_start, + .restore = &raw3215_pm_start, }; #ifdef CONFIG_TN3215_CONSOLE /* * Write a string to the 3215 console */ -static void -con3215_write(struct console *co, const char *str, unsigned int count) +static void con3215_write(struct console *co, const char *str, + unsigned int count) { struct raw3215_info *raw; int i; @@ -768,13 +815,17 @@ static struct tty_driver *con3215_device(struct console *c, int *index) * panic() calls con3215_flush through a panic_notifier * before the system enters a disabled, endless loop. */ -static void -con3215_flush(void) +static void con3215_flush(void) { struct raw3215_info *raw; unsigned long flags; raw = raw3215[0]; /* console 3215 is the first one */ + if (raw->flags & RAW3215_FROZEN) + /* The console is still frozen for suspend. */ + if (ccw_device_force_console()) + /* Forcing didn't work, no panic message .. */ + return; spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw3215_make_room(raw, RAW3215_BUFFER_SIZE); spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); @@ -811,8 +862,7 @@ 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) +static int __init con3215_init(void) { struct ccw_device *cdev; struct raw3215_info *raw; @@ -848,7 +898,7 @@ con3215_init(void) raw->buffer = (char *) alloc_bootmem_low(RAW3215_BUFFER_SIZE); raw->inbuf = (char *) alloc_bootmem_low(RAW3215_INBUF_SIZE); raw->cdev = cdev; - cdev->dev.driver_data = raw; + dev_set_drvdata(&cdev->dev, raw); cdev->handler = raw3215_irq; raw->flags |= RAW3215_FIXED; @@ -875,8 +925,7 @@ console_initcall(con3215_init); * * This routine is called whenever a 3215 tty is opened. */ -static int -tty3215_open(struct tty_struct *tty, struct file * filp) +static int tty3215_open(struct tty_struct *tty, struct file * filp) { struct raw3215_info *raw; int retval, line; @@ -909,8 +958,7 @@ tty3215_open(struct tty_struct *tty, struct file * filp) * This routine is called when the 3215 tty is closed. We wait * for the remaining request to be completed. Then we clean up. */ -static void -tty3215_close(struct tty_struct *tty, struct file * filp) +static void tty3215_close(struct tty_struct *tty, struct file * filp) { struct raw3215_info *raw; @@ -927,8 +975,7 @@ tty3215_close(struct tty_struct *tty, struct file * filp) /* * Returns the amount of free space in the output buffer. */ -static int -tty3215_write_room(struct tty_struct *tty) +static int tty3215_write_room(struct tty_struct *tty) { struct raw3215_info *raw; @@ -944,9 +991,8 @@ tty3215_write_room(struct tty_struct *tty) /* * String write routine for 3215 ttys */ -static int -tty3215_write(struct tty_struct * tty, - const unsigned char *buf, int count) +static int tty3215_write(struct tty_struct * tty, + const unsigned char *buf, int count) { struct raw3215_info *raw; @@ -960,8 +1006,7 @@ tty3215_write(struct tty_struct * tty, /* * Put character routine for 3215 ttys */ -static int -tty3215_put_char(struct tty_struct *tty, unsigned char ch) +static int tty3215_put_char(struct tty_struct *tty, unsigned char ch) { struct raw3215_info *raw; @@ -972,16 +1017,14 @@ tty3215_put_char(struct tty_struct *tty, unsigned char ch) return 1; } -static void -tty3215_flush_chars(struct tty_struct *tty) +static void tty3215_flush_chars(struct tty_struct *tty) { } /* * Returns the number of characters in the output buffer */ -static int -tty3215_chars_in_buffer(struct tty_struct *tty) +static int tty3215_chars_in_buffer(struct tty_struct *tty) { struct raw3215_info *raw; @@ -989,8 +1032,7 @@ tty3215_chars_in_buffer(struct tty_struct *tty) return raw->count; } -static void -tty3215_flush_buffer(struct tty_struct *tty) +static void tty3215_flush_buffer(struct tty_struct *tty) { struct raw3215_info *raw; @@ -1002,9 +1044,8 @@ tty3215_flush_buffer(struct tty_struct *tty) /* * Currently we don't have any io controls for 3215 ttys */ -static int -tty3215_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) +static int tty3215_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; @@ -1019,8 +1060,7 @@ tty3215_ioctl(struct tty_struct *tty, struct file * file, /* * Disable reading from a 3215 tty */ -static void -tty3215_throttle(struct tty_struct * tty) +static void tty3215_throttle(struct tty_struct * tty) { struct raw3215_info *raw; @@ -1031,8 +1071,7 @@ tty3215_throttle(struct tty_struct * tty) /* * Enable reading from a 3215 tty */ -static void -tty3215_unthrottle(struct tty_struct * tty) +static void tty3215_unthrottle(struct tty_struct * tty) { struct raw3215_info *raw; unsigned long flags; @@ -1049,8 +1088,7 @@ tty3215_unthrottle(struct tty_struct * tty) /* * Disable writing to a 3215 tty */ -static void -tty3215_stop(struct tty_struct *tty) +static void tty3215_stop(struct tty_struct *tty) { struct raw3215_info *raw; @@ -1061,8 +1099,7 @@ tty3215_stop(struct tty_struct *tty) /* * Enable writing to a 3215 tty */ -static void -tty3215_start(struct tty_struct *tty) +static void tty3215_start(struct tty_struct *tty) { struct raw3215_info *raw; unsigned long flags; @@ -1096,8 +1133,7 @@ static const struct tty_operations tty3215_ops = { * 3215 tty registration code called from tty_init(). * Most kernel services (incl. kmalloc) are available at this poimt. */ -static int __init -tty3215_init(void) +static int __init tty3215_init(void) { struct tty_driver *driver; int ret; @@ -1142,8 +1178,7 @@ tty3215_init(void) return 0; } -static void __exit -tty3215_exit(void) +static void __exit tty3215_exit(void) { tty_unregister_driver(tty3215_driver); put_tty_driver(tty3215_driver); diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index ed5396dae58..44d02e371c0 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -1,11 +1,10 @@ /* - * drivers/s390/char/con3270.c - * IBM/3270 Driver - console view. + * IBM/3270 Driver - console view. * - * Author(s): - * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) - * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> - * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): + * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) + * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> + * Copyright IBM Corp. 2003, 2009 */ #include <linux/bootmem.h> @@ -530,6 +529,7 @@ con3270_flush(void) cp = condev; if (!cp->view.dev) return; + raw3270_pm_unfreeze(&cp->view); spin_lock_irqsave(&cp->view.lock, flags); con3270_wait_write(cp); cp->nr_up = 0; diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 40759c33477..097d3846a82 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -1,11 +1,10 @@ /* - * drivers/s390/char/fs3270.c - * IBM/3270 Driver - fullscreen driver. + * IBM/3270 Driver - fullscreen driver. * - * Author(s): - * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) - * Rewritten for 2.5/2.6 by Martin Schwidefsky <schwidefsky@de.ibm.com> - * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): + * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) + * Rewritten for 2.5/2.6 by Martin Schwidefsky <schwidefsky@de.ibm.com> + * Copyright IBM Corp. 2003, 2009 */ #include <linux/bootmem.h> @@ -399,6 +398,11 @@ fs3270_free_view(struct raw3270_view *view) static void fs3270_release(struct raw3270_view *view) { + struct fs3270 *fp; + + fp = (struct fs3270 *) view; + if (fp->fs_pid) + kill_pid(fp->fs_pid, SIGHUP, 1); } /* View to a 3270 device. Can be console, tty or fullscreen. */ diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 97e63cf4694..75a8831eebb 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -1,10 +1,9 @@ /* - * drivers/s390/char/monreader.c - * * Character device driver for reading z/VM *MONITOR service records. * - * Copyright IBM Corp. 2004, 2008 - * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> + * Copyright IBM Corp. 2004, 2009 + * + * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> */ #define KMSG_COMPONENT "monreader" @@ -22,6 +21,7 @@ #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/poll.h> +#include <linux/device.h> #include <net/iucv/iucv.h> #include <asm/uaccess.h> #include <asm/ebcdic.h> @@ -78,6 +78,7 @@ static u8 user_data_sever[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; +static struct device *monreader_device; /****************************************************************************** * helper functions * @@ -319,11 +320,12 @@ static int mon_open(struct inode *inode, struct file *filp) goto out_path; } filp->private_data = monpriv; + monreader_device->driver_data = monpriv; unlock_kernel(); return nonseekable_open(inode, filp); out_path: - kfree(monpriv->path); + iucv_path_free(monpriv->path); out_priv: mon_free_mem(monpriv); out_use: @@ -341,10 +343,13 @@ static int mon_close(struct inode *inode, struct file *filp) /* * Close IUCV connection and unregister */ - rc = iucv_path_sever(monpriv->path, user_data_sever); - if (rc) - pr_warning("Disconnecting the z/VM *MONITOR system service " - "failed with rc=%i\n", rc); + if (monpriv->path) { + rc = iucv_path_sever(monpriv->path, user_data_sever); + if (rc) + pr_warning("Disconnecting the z/VM *MONITOR system " + "service failed with rc=%i\n", rc); + iucv_path_free(monpriv->path); + } atomic_set(&monpriv->iucv_severed, 0); atomic_set(&monpriv->iucv_connected, 0); @@ -452,6 +457,94 @@ static struct miscdevice mon_dev = { .minor = MISC_DYNAMIC_MINOR, }; + +/****************************************************************************** + * suspend / resume * + *****************************************************************************/ +static int monreader_freeze(struct device *dev) +{ + struct mon_private *monpriv = dev->driver_data; + int rc; + + if (!monpriv) + return 0; + if (monpriv->path) { + rc = iucv_path_sever(monpriv->path, user_data_sever); + if (rc) + pr_warning("Disconnecting the z/VM *MONITOR system " + "service failed with rc=%i\n", rc); + iucv_path_free(monpriv->path); + } + atomic_set(&monpriv->iucv_severed, 0); + atomic_set(&monpriv->iucv_connected, 0); + atomic_set(&monpriv->read_ready, 0); + atomic_set(&monpriv->msglim_count, 0); + monpriv->write_index = 0; + monpriv->read_index = 0; + monpriv->path = NULL; + return 0; +} + +static int monreader_thaw(struct device *dev) +{ + struct mon_private *monpriv = dev->driver_data; + int rc; + + if (!monpriv) + return 0; + rc = -ENOMEM; + monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL); + if (!monpriv->path) + goto out; + rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler, + MON_SERVICE, NULL, user_data_connect, monpriv); + if (rc) { + pr_err("Connecting to the z/VM *MONITOR system service " + "failed with rc=%i\n", rc); + goto out_path; + } + wait_event(mon_conn_wait_queue, + atomic_read(&monpriv->iucv_connected) || + atomic_read(&monpriv->iucv_severed)); + if (atomic_read(&monpriv->iucv_severed)) + goto out_path; + return 0; +out_path: + rc = -EIO; + iucv_path_free(monpriv->path); + monpriv->path = NULL; +out: + atomic_set(&monpriv->iucv_severed, 1); + return rc; +} + +static int monreader_restore(struct device *dev) +{ + int rc; + + segment_unload(mon_dcss_name); + rc = segment_load(mon_dcss_name, SEGMENT_SHARED, + &mon_dcss_start, &mon_dcss_end); + if (rc < 0) { + segment_warning(rc, mon_dcss_name); + panic("fatal monreader resume error: no monitor dcss\n"); + } + return monreader_thaw(dev); +} + +static struct dev_pm_ops monreader_pm_ops = { + .freeze = monreader_freeze, + .thaw = monreader_thaw, + .restore = monreader_restore, +}; + +static struct device_driver monreader_driver = { + .name = "monreader", + .bus = &iucv_bus, + .pm = &monreader_pm_ops, +}; + + /****************************************************************************** * module init/exit * *****************************************************************************/ @@ -475,16 +568,33 @@ static int __init mon_init(void) return rc; } + rc = driver_register(&monreader_driver); + if (rc) + goto out_iucv; + monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!monreader_device) + goto out_driver; + dev_set_name(monreader_device, "monreader-dev"); + monreader_device->bus = &iucv_bus; + monreader_device->parent = iucv_root; + monreader_device->driver = &monreader_driver; + monreader_device->release = (void (*)(struct device *))kfree; + rc = device_register(monreader_device); + if (rc) { + kfree(monreader_device); + goto out_driver; + } + rc = segment_type(mon_dcss_name); if (rc < 0) { segment_warning(rc, mon_dcss_name); - goto out_iucv; + goto out_device; } if (rc != SEG_TYPE_SC) { pr_err("The specified *MONITOR DCSS %s does not have the " "required type SC\n", mon_dcss_name); rc = -EINVAL; - goto out_iucv; + goto out_device; } rc = segment_load(mon_dcss_name, SEGMENT_SHARED, @@ -492,7 +602,7 @@ static int __init mon_init(void) if (rc < 0) { segment_warning(rc, mon_dcss_name); rc = -EINVAL; - goto out_iucv; + goto out_device; } dcss_mkname(mon_dcss_name, &user_data_connect[8]); @@ -503,6 +613,10 @@ static int __init mon_init(void) out: segment_unload(mon_dcss_name); +out_device: + device_unregister(monreader_device); +out_driver: + driver_unregister(&monreader_driver); out_iucv: iucv_unregister(&monreader_iucv_handler, 1); return rc; @@ -512,6 +626,8 @@ static void __exit mon_exit(void) { segment_unload(mon_dcss_name); WARN_ON(misc_deregister(&mon_dev) != 0); + device_unregister(monreader_device); + driver_unregister(&monreader_driver); iucv_unregister(&monreader_iucv_handler, 1); return; } diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index c7d7483bab9..66fb8eba93f 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -1,9 +1,7 @@ /* - * drivers/s390/char/monwriter.c - * * Character device driver for writing z/VM *MONITOR service records. * - * Copyright (C) IBM Corp. 2006 + * Copyright IBM Corp. 2006, 2009 * * Author(s): Melissa Howland <Melissa.Howland@us.ibm.com> */ @@ -22,6 +20,7 @@ #include <linux/ctype.h> #include <linux/poll.h> #include <linux/mutex.h> +#include <linux/platform_device.h> #include <asm/uaccess.h> #include <asm/ebcdic.h> #include <asm/io.h> @@ -40,7 +39,10 @@ struct mon_buf { char *data; }; +static LIST_HEAD(mon_priv_list); + struct mon_private { + struct list_head priv_list; struct list_head list; struct monwrite_hdr hdr; size_t hdr_to_read; @@ -188,6 +190,7 @@ static int monwrite_open(struct inode *inode, struct file *filp) 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); } @@ -206,6 +209,7 @@ static int monwrite_close(struct inode *inode, struct file *filp) kfree(entry->data); kfree(entry); } + list_del(&monpriv->priv_list); kfree(monpriv); return 0; } @@ -281,20 +285,102 @@ static struct miscdevice mon_dev = { }; /* + * suspend/resume + */ + +static int monwriter_freeze(struct device *dev) +{ + struct mon_private *monpriv; + struct mon_buf *monbuf; + + list_for_each_entry(monpriv, &mon_priv_list, priv_list) { + list_for_each_entry(monbuf, &monpriv->list, list) { + if (monbuf->hdr.mon_function != MONWRITE_GEN_EVENT) + monwrite_diag(&monbuf->hdr, monbuf->data, + APPLDATA_STOP_REC); + } + } + return 0; +} + +static int monwriter_restore(struct device *dev) +{ + struct mon_private *monpriv; + struct mon_buf *monbuf; + + list_for_each_entry(monpriv, &mon_priv_list, priv_list) { + list_for_each_entry(monbuf, &monpriv->list, list) { + if (monbuf->hdr.mon_function == MONWRITE_START_INTERVAL) + monwrite_diag(&monbuf->hdr, monbuf->data, + APPLDATA_START_INTERVAL_REC); + if (monbuf->hdr.mon_function == MONWRITE_START_CONFIG) + monwrite_diag(&monbuf->hdr, monbuf->data, + APPLDATA_START_CONFIG_REC); + } + } + return 0; +} + +static int monwriter_thaw(struct device *dev) +{ + return monwriter_restore(dev); +} + +static struct dev_pm_ops monwriter_pm_ops = { + .freeze = monwriter_freeze, + .thaw = monwriter_thaw, + .restore = monwriter_restore, +}; + +static struct platform_driver monwriter_pdrv = { + .driver = { + .name = "monwriter", + .owner = THIS_MODULE, + .pm = &monwriter_pm_ops, + }, +}; + +static struct platform_device *monwriter_pdev; + +/* * module init/exit */ static int __init mon_init(void) { - if (MACHINE_IS_VM) - return misc_register(&mon_dev); - else + int rc; + + if (!MACHINE_IS_VM) return -ENODEV; + + rc = platform_driver_register(&monwriter_pdrv); + if (rc) + return rc; + + monwriter_pdev = platform_device_register_simple("monwriter", -1, NULL, + 0); + if (IS_ERR(monwriter_pdev)) { + rc = PTR_ERR(monwriter_pdev); + goto out_driver; + } + + rc = misc_register(&mon_dev); + if (rc) + goto out_device; + return 0; + +out_device: + platform_device_unregister(monwriter_pdev); +out_driver: + platform_driver_unregister(&monwriter_pdrv); + return rc; } static void __exit mon_exit(void) { WARN_ON(misc_deregister(&mon_dev) != 0); + platform_device_unregister(monwriter_pdev); + platform_driver_unregister(&monwriter_pdrv); } module_init(mon_init); diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 0b15cf107ec..acab7b2dfe8 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -1,11 +1,10 @@ /* - * drivers/s390/char/raw3270.c - * IBM/3270 Driver - core functions. + * IBM/3270 Driver - core functions. * - * Author(s): - * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) - * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> - * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): + * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) + * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> + * Copyright IBM Corp. 2003, 2009 */ #include <linux/bootmem.h> @@ -61,6 +60,7 @@ struct raw3270 { #define RAW3270_FLAGS_ATTN 2 /* Device sent an ATTN interrupt */ #define RAW3270_FLAGS_READY 4 /* Device is useable by views */ #define RAW3270_FLAGS_CONSOLE 8 /* Device is the console. */ +#define RAW3270_FLAGS_FROZEN 16 /* set if 3270 is frozen for suspend */ /* Semaphore to protect global data of raw3270 (devices, views, etc). */ static DEFINE_MUTEX(raw3270_mutex); @@ -306,7 +306,8 @@ raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); rp = view->dev; - if (!rp || rp->view != view) + if (!rp || rp->view != view || + test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) rc = -EACCES; else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) rc = -ENODEV; @@ -323,7 +324,8 @@ raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) int rc; rp = view->dev; - if (!rp || rp->view != view) + if (!rp || rp->view != view || + test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) rc = -EACCES; else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) rc = -ENODEV; @@ -355,7 +357,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) struct raw3270_request *rq; int rc; - rp = (struct raw3270 *) cdev->dev.driver_data; + rp = dev_get_drvdata(&cdev->dev); if (!rp) return; rq = (struct raw3270_request *) intparm; @@ -764,7 +766,8 @@ raw3270_reset(struct raw3270_view *view) int rc; rp = view->dev; - if (!rp || rp->view != view) + if (!rp || rp->view != view || + test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) rc = -EACCES; else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) rc = -ENODEV; @@ -828,7 +831,7 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) if (rp->minor == -1) return -EUSERS; rp->cdev = cdev; - cdev->dev.driver_data = rp; + dev_set_drvdata(&cdev->dev, rp); cdev->handler = raw3270_irq; return 0; } @@ -922,6 +925,8 @@ raw3270_activate_view(struct raw3270_view *view) rc = 0; else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) rc = -ENODEV; + else if (test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) + rc = -EACCES; else { oldview = NULL; if (rp->view) { @@ -969,7 +974,8 @@ raw3270_deactivate_view(struct raw3270_view *view) list_del_init(&view->list); list_add_tail(&view->list, &rp->view_list); /* Try to activate another view. */ - if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) { + if (test_bit(RAW3270_FLAGS_READY, &rp->flags) && + !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) { list_for_each_entry(view, &rp->view_list, list) { rp->view = view; if (view->fn->activate(view) == 0) @@ -1068,7 +1074,8 @@ raw3270_del_view(struct raw3270_view *view) rp->view = NULL; } list_del_init(&view->list); - if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags)) { + if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags) && + !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) { /* Try to activate another view. */ list_for_each_entry(nv, &rp->view_list, list) { if (nv->fn->activate(nv) == 0) { @@ -1105,7 +1112,7 @@ raw3270_delete_device(struct raw3270 *rp) /* Disconnect from ccw_device. */ cdev = rp->cdev; rp->cdev = NULL; - cdev->dev.driver_data = NULL; + dev_set_drvdata(&cdev->dev, NULL); cdev->handler = NULL; /* Put ccw_device structure. */ @@ -1129,7 +1136,7 @@ static ssize_t raw3270_model_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%i\n", - ((struct raw3270 *) dev->driver_data)->model); + ((struct raw3270 *) dev_get_drvdata(dev))->model); } static DEVICE_ATTR(model, 0444, raw3270_model_show, NULL); @@ -1137,7 +1144,7 @@ static ssize_t raw3270_rows_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%i\n", - ((struct raw3270 *) dev->driver_data)->rows); + ((struct raw3270 *) dev_get_drvdata(dev))->rows); } static DEVICE_ATTR(rows, 0444, raw3270_rows_show, NULL); @@ -1145,7 +1152,7 @@ static ssize_t raw3270_columns_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%i\n", - ((struct raw3270 *) dev->driver_data)->cols); + ((struct raw3270 *) dev_get_drvdata(dev))->cols); } static DEVICE_ATTR(columns, 0444, raw3270_columns_show, NULL); @@ -1282,7 +1289,7 @@ raw3270_remove (struct ccw_device *cdev) struct raw3270_view *v; struct raw3270_notifier *np; - rp = cdev->dev.driver_data; + rp = dev_get_drvdata(&cdev->dev); /* * _remove is the opposite of _probe; it's probe that * should set up rp. raw3270_remove gets entered for @@ -1330,13 +1337,65 @@ raw3270_set_offline (struct ccw_device *cdev) { struct raw3270 *rp; - rp = cdev->dev.driver_data; + rp = dev_get_drvdata(&cdev->dev); if (test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags)) return -EBUSY; raw3270_remove(cdev); return 0; } +static int raw3270_pm_stop(struct ccw_device *cdev) +{ + struct raw3270 *rp; + struct raw3270_view *view; + unsigned long flags; + + rp = cdev->dev.driver_data; + if (!rp) + return 0; + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + if (rp->view) + rp->view->fn->deactivate(rp->view); + if (!test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags)) { + /* + * Release tty and fullscreen for all non-console + * devices. + */ + list_for_each_entry(view, &rp->view_list, list) { + if (view->fn->release) + view->fn->release(view); + } + } + set_bit(RAW3270_FLAGS_FROZEN, &rp->flags); + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); + return 0; +} + +static int raw3270_pm_start(struct ccw_device *cdev) +{ + struct raw3270 *rp; + unsigned long flags; + + rp = cdev->dev.driver_data; + if (!rp) + return 0; + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + clear_bit(RAW3270_FLAGS_FROZEN, &rp->flags); + if (rp->view) + rp->view->fn->activate(rp->view); + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); + return 0; +} + +void raw3270_pm_unfreeze(struct raw3270_view *view) +{ + struct raw3270 *rp; + + rp = view->dev; + if (rp && test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) + ccw_device_force_console(); +} + static struct ccw_device_id raw3270_id[] = { { CCW_DEVICE(0x3270, 0) }, { CCW_DEVICE(0x3271, 0) }, @@ -1360,6 +1419,9 @@ static struct ccw_driver raw3270_ccw_driver = { .remove = &raw3270_remove, .set_online = &raw3270_set_online, .set_offline = &raw3270_set_offline, + .freeze = &raw3270_pm_stop, + .thaw = &raw3270_pm_start, + .restore = &raw3270_pm_start, }; static int diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 90beaa80a78..ed34eb2199c 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -1,11 +1,10 @@ /* - * drivers/s390/char/raw3270.h - * IBM/3270 Driver + * IBM/3270 Driver * - * Author(s): - * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) - * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> - * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): + * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) + * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> + * Copyright IBM Corp. 2003, 2009 */ #include <asm/idals.h> @@ -195,6 +194,7 @@ void raw3270_wait_cons_dev(struct raw3270 *); /* Notifier for device addition/removal */ int raw3270_register_notifier(void (*notifier)(int, int)); void raw3270_unregister_notifier(void (*notifier)(int, int)); +void raw3270_pm_unfreeze(struct raw3270_view *); /* * Little memory allocator for string objects. diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 4377e93a43d..a983f508678 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -1,11 +1,10 @@ /* - * drivers/s390/char/sclp.c - * core function to access sclp interface + * core function to access sclp interface * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke <mpeschke@de.ibm.com> - * Martin Schwidefsky <schwidefsky@de.ibm.com> + * Copyright IBM Corp. 1999, 2009 + * + * Author(s): Martin Peschke <mpeschke@de.ibm.com> + * Martin Schwidefsky <schwidefsky@de.ibm.com> */ #include <linux/module.h> @@ -16,6 +15,9 @@ #include <linux/reboot.h> #include <linux/jiffies.h> #include <linux/init.h> +#include <linux/suspend.h> +#include <linux/completion.h> +#include <linux/platform_device.h> #include <asm/types.h> #include <asm/s390_ext.h> @@ -47,6 +49,16 @@ static struct sclp_req sclp_init_req; static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +/* Suspend request */ +static DECLARE_COMPLETION(sclp_request_queue_flushed); + +static void sclp_suspend_req_cb(struct sclp_req *req, void *data) +{ + complete(&sclp_request_queue_flushed); +} + +static struct sclp_req sclp_suspend_req; + /* Timer for request retries. */ static struct timer_list sclp_request_timer; @@ -84,6 +96,12 @@ static volatile enum sclp_mask_state_t { sclp_mask_state_initializing } sclp_mask_state = sclp_mask_state_idle; +/* Internal state: is the driver suspended? */ +static enum sclp_suspend_state_t { + sclp_suspend_state_running, + sclp_suspend_state_suspended, +} sclp_suspend_state = sclp_suspend_state_running; + /* Maximum retry counts */ #define SCLP_INIT_RETRY 3 #define SCLP_MASK_RETRY 3 @@ -211,6 +229,8 @@ sclp_process_queue(void) del_timer(&sclp_request_timer); while (!list_empty(&sclp_req_queue)) { req = list_entry(sclp_req_queue.next, struct sclp_req, list); + if (!req->sccb) + goto do_post; rc = __sclp_start_request(req); if (rc == 0) break; @@ -222,6 +242,7 @@ sclp_process_queue(void) sclp_request_timeout, 0); break; } +do_post: /* Post-processing for aborted request */ list_del(&req->list); if (req->callback) { @@ -233,6 +254,19 @@ sclp_process_queue(void) spin_unlock_irqrestore(&sclp_lock, flags); } +static int __sclp_can_add_request(struct sclp_req *req) +{ + if (req == &sclp_suspend_req || req == &sclp_init_req) + return 1; + if (sclp_suspend_state != sclp_suspend_state_running) + return 0; + if (sclp_init_state != sclp_init_state_initialized) + return 0; + if (sclp_activation_state != sclp_activation_state_active) + return 0; + return 1; +} + /* Queue a new request. Return zero on success, non-zero otherwise. */ int sclp_add_request(struct sclp_req *req) @@ -241,9 +275,7 @@ sclp_add_request(struct sclp_req *req) int rc; spin_lock_irqsave(&sclp_lock, flags); - if ((sclp_init_state != sclp_init_state_initialized || - sclp_activation_state != sclp_activation_state_active) && - req != &sclp_init_req) { + if (!__sclp_can_add_request(req)) { spin_unlock_irqrestore(&sclp_lock, flags); return -EIO; } @@ -254,10 +286,16 @@ sclp_add_request(struct sclp_req *req) /* Start if request is first in list */ if (sclp_running_state == sclp_running_state_idle && req->list.prev == &sclp_req_queue) { + if (!req->sccb) { + list_del(&req->list); + rc = -ENODATA; + goto out; + } rc = __sclp_start_request(req); if (rc) list_del(&req->list); } +out: spin_unlock_irqrestore(&sclp_lock, flags); return rc; } @@ -560,6 +598,7 @@ sclp_register(struct sclp_register *reg) /* Trigger initial state change callback */ reg->sclp_receive_mask = 0; reg->sclp_send_mask = 0; + reg->pm_event_posted = 0; list_add(®->list, &sclp_reg_list); spin_unlock_irqrestore(&sclp_lock, flags); rc = sclp_init_mask(1); @@ -880,20 +919,134 @@ static struct notifier_block sclp_reboot_notifier = { .notifier_call = sclp_reboot_event }; +/* + * Suspend/resume SCLP notifier implementation + */ + +static void sclp_pm_event(enum sclp_pm_event sclp_pm_event, int rollback) +{ + struct sclp_register *reg; + unsigned long flags; + + if (!rollback) { + spin_lock_irqsave(&sclp_lock, flags); + list_for_each_entry(reg, &sclp_reg_list, list) + reg->pm_event_posted = 0; + spin_unlock_irqrestore(&sclp_lock, flags); + } + do { + spin_lock_irqsave(&sclp_lock, flags); + list_for_each_entry(reg, &sclp_reg_list, list) { + if (rollback && reg->pm_event_posted) + goto found; + if (!rollback && !reg->pm_event_posted) + goto found; + } + spin_unlock_irqrestore(&sclp_lock, flags); + return; +found: + spin_unlock_irqrestore(&sclp_lock, flags); + if (reg->pm_event_fn) + reg->pm_event_fn(reg, sclp_pm_event); + reg->pm_event_posted = rollback ? 0 : 1; + } while (1); +} + +/* + * Susend/resume callbacks for platform device + */ + +static int sclp_freeze(struct device *dev) +{ + unsigned long flags; + int rc; + + sclp_pm_event(SCLP_PM_EVENT_FREEZE, 0); + + spin_lock_irqsave(&sclp_lock, flags); + sclp_suspend_state = sclp_suspend_state_suspended; + spin_unlock_irqrestore(&sclp_lock, flags); + + /* Init supend data */ + memset(&sclp_suspend_req, 0, sizeof(sclp_suspend_req)); + sclp_suspend_req.callback = sclp_suspend_req_cb; + sclp_suspend_req.status = SCLP_REQ_FILLED; + init_completion(&sclp_request_queue_flushed); + + rc = sclp_add_request(&sclp_suspend_req); + if (rc == 0) + wait_for_completion(&sclp_request_queue_flushed); + else if (rc != -ENODATA) + goto fail_thaw; + + rc = sclp_deactivate(); + if (rc) + goto fail_thaw; + return 0; + +fail_thaw: + spin_lock_irqsave(&sclp_lock, flags); + sclp_suspend_state = sclp_suspend_state_running; + spin_unlock_irqrestore(&sclp_lock, flags); + sclp_pm_event(SCLP_PM_EVENT_THAW, 1); + return rc; +} + +static int sclp_undo_suspend(enum sclp_pm_event event) +{ + unsigned long flags; + int rc; + + rc = sclp_reactivate(); + if (rc) + return rc; + + spin_lock_irqsave(&sclp_lock, flags); + sclp_suspend_state = sclp_suspend_state_running; + spin_unlock_irqrestore(&sclp_lock, flags); + + sclp_pm_event(event, 0); + return 0; +} + +static int sclp_thaw(struct device *dev) +{ + return sclp_undo_suspend(SCLP_PM_EVENT_THAW); +} + +static int sclp_restore(struct device *dev) +{ + return sclp_undo_suspend(SCLP_PM_EVENT_RESTORE); +} + +static struct dev_pm_ops sclp_pm_ops = { + .freeze = sclp_freeze, + .thaw = sclp_thaw, + .restore = sclp_restore, +}; + +static struct platform_driver sclp_pdrv = { + .driver = { + .name = "sclp", + .owner = THIS_MODULE, + .pm = &sclp_pm_ops, + }, +}; + +static struct platform_device *sclp_pdev; + /* Initialize SCLP driver. Return zero if driver is operational, non-zero * otherwise. */ static int sclp_init(void) { unsigned long flags; - int rc; + int rc = 0; spin_lock_irqsave(&sclp_lock, flags); /* Check for previous or running initialization */ - if (sclp_init_state != sclp_init_state_uninitialized) { - spin_unlock_irqrestore(&sclp_lock, flags); - return 0; - } + if (sclp_init_state != sclp_init_state_uninitialized) + goto fail_unlock; sclp_init_state = sclp_init_state_initializing; /* Set up variables */ INIT_LIST_HEAD(&sclp_req_queue); @@ -904,27 +1057,17 @@ sclp_init(void) spin_unlock_irqrestore(&sclp_lock, flags); rc = sclp_check_interface(); spin_lock_irqsave(&sclp_lock, flags); - if (rc) { - sclp_init_state = sclp_init_state_uninitialized; - spin_unlock_irqrestore(&sclp_lock, flags); - return rc; - } + if (rc) + goto fail_init_state_uninitialized; /* Register reboot handler */ rc = register_reboot_notifier(&sclp_reboot_notifier); - if (rc) { - sclp_init_state = sclp_init_state_uninitialized; - spin_unlock_irqrestore(&sclp_lock, flags); - return rc; - } + if (rc) + goto fail_init_state_uninitialized; /* Register interrupt handler */ rc = register_early_external_interrupt(0x2401, sclp_interrupt_handler, &ext_int_info_hwc); - if (rc) { - unregister_reboot_notifier(&sclp_reboot_notifier); - sclp_init_state = sclp_init_state_uninitialized; - spin_unlock_irqrestore(&sclp_lock, flags); - return rc; - } + if (rc) + goto fail_unregister_reboot_notifier; sclp_init_state = sclp_init_state_initialized; spin_unlock_irqrestore(&sclp_lock, flags); /* Enable service-signal external interruption - needs to happen with @@ -932,11 +1075,56 @@ sclp_init(void) ctl_set_bit(0, 9); sclp_init_mask(1); return 0; + +fail_unregister_reboot_notifier: + unregister_reboot_notifier(&sclp_reboot_notifier); +fail_init_state_uninitialized: + sclp_init_state = sclp_init_state_uninitialized; +fail_unlock: + spin_unlock_irqrestore(&sclp_lock, flags); + return rc; } +/* + * SCLP panic notifier: If we are suspended, we thaw SCLP in order to be able + * to print the panic message. + */ +static int sclp_panic_notify(struct notifier_block *self, + unsigned long event, void *data) +{ + if (sclp_suspend_state == sclp_suspend_state_suspended) + sclp_undo_suspend(SCLP_PM_EVENT_THAW); + return NOTIFY_OK; +} + +static struct notifier_block sclp_on_panic_nb = { + .notifier_call = sclp_panic_notify, + .priority = SCLP_PANIC_PRIO, +}; + static __init int sclp_initcall(void) { + int rc; + + rc = platform_driver_register(&sclp_pdrv); + if (rc) + return rc; + sclp_pdev = platform_device_register_simple("sclp", -1, NULL, 0); + rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; + if (rc) + goto fail_platform_driver_unregister; + rc = atomic_notifier_chain_register(&panic_notifier_list, + &sclp_on_panic_nb); + if (rc) + goto fail_platform_device_unregister; + return sclp_init(); + +fail_platform_device_unregister: + platform_device_unregister(sclp_pdev); +fail_platform_driver_unregister: + platform_driver_unregister(&sclp_pdrv); + return rc; } arch_initcall(sclp_initcall); diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index bac80e856f9..60e7cb07095 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -1,10 +1,8 @@ /* - * drivers/s390/char/sclp.h + * Copyright IBM Corp. 1999, 2009 * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke <mpeschke@de.ibm.com> - * Martin Schwidefsky <schwidefsky@de.ibm.com> + * Author(s): Martin Peschke <mpeschke@de.ibm.com> + * Martin Schwidefsky <schwidefsky@de.ibm.com> */ #ifndef __SCLP_H__ @@ -17,7 +15,7 @@ /* maximum number of pages concerning our own memory management */ #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) -#define MAX_CONSOLE_PAGES 4 +#define MAX_CONSOLE_PAGES 6 #define EVTYP_OPCMD 0x01 #define EVTYP_MSG 0x02 @@ -68,6 +66,15 @@ typedef unsigned int sclp_cmdw_t; #define GDS_KEY_SELFDEFTEXTMSG 0x31 +enum sclp_pm_event { + SCLP_PM_EVENT_FREEZE, + SCLP_PM_EVENT_THAW, + SCLP_PM_EVENT_RESTORE, +}; + +#define SCLP_PANIC_PRIO 1 +#define SCLP_PANIC_PRIO_CLIENT 0 + typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */ struct sccb_header { @@ -134,6 +141,10 @@ struct sclp_register { void (*state_change_fn)(struct sclp_register *); /* called for events in cp_receive_mask/sclp_receive_mask */ void (*receiver_fn)(struct evbuf_header *); + /* called for power management events */ + void (*pm_event_fn)(struct sclp_register *, enum sclp_pm_event); + /* pm event posted flag */ + int pm_event_posted; }; /* externals from sclp.c */ diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 77ab6e34a10..5cc11c636d3 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -1,9 +1,8 @@ /* - * drivers/s390/char/sclp_cmd.c + * Copyright IBM Corp. 2007, 2009 * - * Copyright IBM Corp. 2007 - * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, - * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, + * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> */ #define KMSG_COMPONENT "sclp_cmd" @@ -12,11 +11,13 @@ #include <linux/completion.h> #include <linux/init.h> #include <linux/errno.h> +#include <linux/err.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/mmzone.h> #include <linux/memory.h> +#include <linux/platform_device.h> #include <asm/chpid.h> #include <asm/sclp.h> #include <asm/setup.h> @@ -292,6 +293,7 @@ static DEFINE_MUTEX(sclp_mem_mutex); static LIST_HEAD(sclp_mem_list); static u8 sclp_max_storage_id; static unsigned long sclp_storage_ids[256 / BITS_PER_LONG]; +static int sclp_mem_state_changed; struct memory_increment { struct list_head list; @@ -450,6 +452,8 @@ static int sclp_mem_notifier(struct notifier_block *nb, rc = -EINVAL; break; } + if (!rc) + sclp_mem_state_changed = 1; mutex_unlock(&sclp_mem_mutex); return rc ? NOTIFY_BAD : NOTIFY_OK; } @@ -525,6 +529,14 @@ static void __init insert_increment(u16 rn, int standby, int assigned) list_add(&new_incr->list, prev); } +static int sclp_mem_freeze(struct device *dev) +{ + if (!sclp_mem_state_changed) + return 0; + pr_err("Memory hotplug state changed, suspend refused.\n"); + return -EPERM; +} + struct read_storage_sccb { struct sccb_header header; u16 max_id; @@ -534,8 +546,20 @@ struct read_storage_sccb { u32 entries[0]; } __packed; +static struct dev_pm_ops sclp_mem_pm_ops = { + .freeze = sclp_mem_freeze, +}; + +static struct platform_driver sclp_mem_pdrv = { + .driver = { + .name = "sclp_mem", + .pm = &sclp_mem_pm_ops, + }, +}; + static int __init sclp_detect_standby_memory(void) { + struct platform_device *sclp_pdev; struct read_storage_sccb *sccb; int i, id, assigned, rc; @@ -588,7 +612,17 @@ static int __init sclp_detect_standby_memory(void) rc = register_memory_notifier(&sclp_mem_nb); if (rc) goto out; + rc = platform_driver_register(&sclp_mem_pdrv); + if (rc) + goto out; + sclp_pdev = platform_device_register_simple("sclp_mem", -1, NULL, 0); + rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; + if (rc) + goto out_driver; sclp_add_standby_memory(); + goto out; +out_driver: + platform_driver_unregister(&sclp_mem_pdrv); out: free_page((unsigned long) sccb); return rc; diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index 9a25c4bd142..336811a7767 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c @@ -1,11 +1,9 @@ /* - * drivers/s390/char/sclp_con.c - * SCLP line mode console driver + * SCLP line mode console driver * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke <mpeschke@de.ibm.com> - * Martin Schwidefsky <schwidefsky@de.ibm.com> + * Copyright IBM Corp. 1999, 2009 + * Author(s): Martin Peschke <mpeschke@de.ibm.com> + * Martin Schwidefsky <schwidefsky@de.ibm.com> */ #include <linux/kmod.h> @@ -32,13 +30,14 @@ static spinlock_t sclp_con_lock; static struct list_head sclp_con_pages; /* List of full struct sclp_buffer structures ready for output */ static struct list_head sclp_con_outqueue; -/* Counter how many buffers are emitted (max 1) and how many */ -/* are on the output queue. */ -static int sclp_con_buffer_count; /* Pointer to current console buffer */ static struct sclp_buffer *sclp_conbuf; /* Timer for delayed output of console messages */ static struct timer_list sclp_con_timer; +/* Suspend mode flag */ +static int sclp_con_suspended; +/* Flag that output queue is currently running */ +static int sclp_con_queue_running; /* Output format for console messages */ static unsigned short sclp_con_columns; @@ -53,42 +52,71 @@ sclp_conbuf_callback(struct sclp_buffer *buffer, int rc) do { page = sclp_unmake_buffer(buffer); spin_lock_irqsave(&sclp_con_lock, flags); + /* Remove buffer from outqueue */ list_del(&buffer->list); - sclp_con_buffer_count--; list_add_tail((struct list_head *) page, &sclp_con_pages); + /* Check if there is a pending buffer on the out queue. */ buffer = NULL; if (!list_empty(&sclp_con_outqueue)) - buffer = list_entry(sclp_con_outqueue.next, - struct sclp_buffer, list); + buffer = list_first_entry(&sclp_con_outqueue, + struct sclp_buffer, list); + if (!buffer || sclp_con_suspended) { + sclp_con_queue_running = 0; + spin_unlock_irqrestore(&sclp_con_lock, flags); + break; + } spin_unlock_irqrestore(&sclp_con_lock, flags); - } while (buffer && sclp_emit_buffer(buffer, sclp_conbuf_callback)); + } while (sclp_emit_buffer(buffer, sclp_conbuf_callback)); } -static void -sclp_conbuf_emit(void) +/* + * Finalize and emit first pending buffer. + */ +static void sclp_conbuf_emit(void) { struct sclp_buffer* buffer; unsigned long flags; - int count; int rc; spin_lock_irqsave(&sclp_con_lock, flags); - buffer = sclp_conbuf; + if (sclp_conbuf) + list_add_tail(&sclp_conbuf->list, &sclp_con_outqueue); sclp_conbuf = NULL; - if (buffer == NULL) { - spin_unlock_irqrestore(&sclp_con_lock, flags); - return; - } - list_add_tail(&buffer->list, &sclp_con_outqueue); - count = sclp_con_buffer_count++; + if (sclp_con_queue_running || sclp_con_suspended) + goto out_unlock; + if (list_empty(&sclp_con_outqueue)) + goto out_unlock; + buffer = list_first_entry(&sclp_con_outqueue, struct sclp_buffer, + list); + sclp_con_queue_running = 1; spin_unlock_irqrestore(&sclp_con_lock, flags); - if (count) - return; + rc = sclp_emit_buffer(buffer, sclp_conbuf_callback); if (rc) sclp_conbuf_callback(buffer, rc); + return; +out_unlock: + spin_unlock_irqrestore(&sclp_con_lock, flags); +} + +/* + * Wait until out queue is empty + */ +static void sclp_console_sync_queue(void) +{ + unsigned long flags; + + spin_lock_irqsave(&sclp_con_lock, flags); + if (timer_pending(&sclp_con_timer)) + del_timer_sync(&sclp_con_timer); + while (sclp_con_queue_running) { + spin_unlock_irqrestore(&sclp_con_lock, flags); + sclp_sync_wait(); + spin_lock_irqsave(&sclp_con_lock, flags); + } + spin_unlock_irqrestore(&sclp_con_lock, flags); } /* @@ -123,6 +151,8 @@ sclp_console_write(struct console *console, const char *message, /* make sure we have a console output buffer */ if (sclp_conbuf == NULL) { while (list_empty(&sclp_con_pages)) { + if (sclp_con_suspended) + goto out; spin_unlock_irqrestore(&sclp_con_lock, flags); sclp_sync_wait(); spin_lock_irqsave(&sclp_con_lock, flags); @@ -157,6 +187,7 @@ sclp_console_write(struct console *console, const char *message, sclp_con_timer.expires = jiffies + HZ/10; add_timer(&sclp_con_timer); } +out: spin_unlock_irqrestore(&sclp_con_lock, flags); } @@ -168,30 +199,43 @@ sclp_console_device(struct console *c, int *index) } /* - * This routine is called from panic when the kernel - * is going to give up. We have to make sure that all buffers - * will be flushed to the SCLP. + * Make sure that all buffers will be flushed to the SCLP. */ static void sclp_console_flush(void) { + sclp_conbuf_emit(); + sclp_console_sync_queue(); +} + +/* + * Resume console: If there are cached messages, emit them. + */ +static void sclp_console_resume(void) +{ unsigned long flags; + spin_lock_irqsave(&sclp_con_lock, flags); + sclp_con_suspended = 0; + spin_unlock_irqrestore(&sclp_con_lock, flags); sclp_conbuf_emit(); +} + +/* + * Suspend console: Set suspend flag and flush console + */ +static void sclp_console_suspend(void) +{ + unsigned long flags; + spin_lock_irqsave(&sclp_con_lock, flags); - if (timer_pending(&sclp_con_timer)) - del_timer(&sclp_con_timer); - while (sclp_con_buffer_count > 0) { - spin_unlock_irqrestore(&sclp_con_lock, flags); - sclp_sync_wait(); - spin_lock_irqsave(&sclp_con_lock, flags); - } + sclp_con_suspended = 1; spin_unlock_irqrestore(&sclp_con_lock, flags); + sclp_console_flush(); } -static int -sclp_console_notify(struct notifier_block *self, - unsigned long event, void *data) +static int sclp_console_notify(struct notifier_block *self, + unsigned long event, void *data) { sclp_console_flush(); return NOTIFY_OK; @@ -199,7 +243,7 @@ sclp_console_notify(struct notifier_block *self, static struct notifier_block on_panic_nb = { .notifier_call = sclp_console_notify, - .priority = 1, + .priority = SCLP_PANIC_PRIO_CLIENT, }; static struct notifier_block on_reboot_nb = { @@ -221,6 +265,22 @@ static struct console sclp_console = }; /* + * This function is called for SCLP suspend and resume events. + */ +void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event) +{ + switch (sclp_pm_event) { + case SCLP_PM_EVENT_FREEZE: + sclp_console_suspend(); + break; + case SCLP_PM_EVENT_RESTORE: + case SCLP_PM_EVENT_THAW: + sclp_console_resume(); + break; + } +} + +/* * called by console_init() in drivers/char/tty_io.c at boot-time. */ static int __init @@ -243,7 +303,6 @@ sclp_console_init(void) } INIT_LIST_HEAD(&sclp_con_outqueue); spin_lock_init(&sclp_con_lock); - sclp_con_buffer_count = 0; sclp_conbuf = NULL; init_timer(&sclp_con_timer); diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index 710af42603f..4be63be7344 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c @@ -1,11 +1,10 @@ /* - * drivers/s390/char/sclp_rw.c - * driver: reading from and writing to system console on S/390 via SCLP + * driver: reading from and writing to system console on S/390 via SCLP * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke <mpeschke@de.ibm.com> - * Martin Schwidefsky <schwidefsky@de.ibm.com> + * Copyright IBM Corp. 1999, 2009 + * + * Author(s): Martin Peschke <mpeschke@de.ibm.com> + * Martin Schwidefsky <schwidefsky@de.ibm.com> */ #include <linux/kmod.h> @@ -26,9 +25,16 @@ */ #define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer)) +static void sclp_rw_pm_event(struct sclp_register *reg, + enum sclp_pm_event sclp_pm_event) +{ + sclp_console_pm_event(sclp_pm_event); +} + /* Event type structure for write message and write priority message */ static struct sclp_register sclp_rw_event = { - .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK + .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK, + .pm_event_fn = sclp_rw_pm_event, }; /* diff --git a/drivers/s390/char/sclp_rw.h b/drivers/s390/char/sclp_rw.h index 6aa7a6948bc..85f491ea929 100644 --- a/drivers/s390/char/sclp_rw.h +++ b/drivers/s390/char/sclp_rw.h @@ -1,11 +1,10 @@ /* - * drivers/s390/char/sclp_rw.h - * interface to the SCLP-read/write driver + * interface to the SCLP-read/write driver * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke <mpeschke@de.ibm.com> - * Martin Schwidefsky <schwidefsky@de.ibm.com> + * Copyright IBM Corporation 1999, 2009 + * + * Author(s): Martin Peschke <mpeschke@de.ibm.com> + * Martin Schwidefsky <schwidefsky@de.ibm.com> */ #ifndef __SCLP_RW_H__ @@ -93,4 +92,5 @@ void sclp_set_columns(struct sclp_buffer *, unsigned short); void sclp_set_htab(struct sclp_buffer *, unsigned short); int sclp_chars_in_buffer(struct sclp_buffer *); +void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event); #endif /* __SCLP_RW_H__ */ diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index a839aa531d7..5518e24946a 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -1,10 +1,9 @@ /* - * drivers/s390/char/sclp_vt220.c - * SCLP VT220 terminal driver. + * SCLP VT220 terminal driver. * - * S390 version - * Copyright IBM Corp. 2003,2008 - * Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com> + * Copyright IBM Corp. 2003, 2009 + * + * Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com> */ #include <linux/module.h> @@ -69,8 +68,11 @@ static struct list_head sclp_vt220_empty; /* List of pending requests */ static struct list_head sclp_vt220_outqueue; -/* Number of requests in outqueue */ -static int sclp_vt220_outqueue_count; +/* Suspend mode flag */ +static int sclp_vt220_suspended; + +/* Flag that output queue is currently running */ +static int sclp_vt220_queue_running; /* Timer used for delaying write requests to merge subsequent messages into * a single buffer */ @@ -92,6 +94,8 @@ static int __initdata sclp_vt220_init_count; static int sclp_vt220_flush_later; static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf); +static void sclp_vt220_pm_event_fn(struct sclp_register *reg, + enum sclp_pm_event sclp_pm_event); static int __sclp_vt220_emit(struct sclp_vt220_request *request); static void sclp_vt220_emit_current(void); @@ -100,7 +104,8 @@ static struct sclp_register sclp_vt220_register = { .send_mask = EVTYP_VT220MSG_MASK, .receive_mask = EVTYP_VT220MSG_MASK, .state_change_fn = NULL, - .receiver_fn = sclp_vt220_receiver_fn + .receiver_fn = sclp_vt220_receiver_fn, + .pm_event_fn = sclp_vt220_pm_event_fn, }; @@ -120,15 +125,19 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request) spin_lock_irqsave(&sclp_vt220_lock, flags); /* Move request from outqueue to empty queue */ list_del(&request->list); - sclp_vt220_outqueue_count--; list_add_tail((struct list_head *) page, &sclp_vt220_empty); /* Check if there is a pending buffer on the out queue. */ request = NULL; if (!list_empty(&sclp_vt220_outqueue)) request = list_entry(sclp_vt220_outqueue.next, struct sclp_vt220_request, list); + if (!request || sclp_vt220_suspended) { + sclp_vt220_queue_running = 0; + spin_unlock_irqrestore(&sclp_vt220_lock, flags); + break; + } spin_unlock_irqrestore(&sclp_vt220_lock, flags); - } while (request && __sclp_vt220_emit(request)); + } while (__sclp_vt220_emit(request)); if (request == NULL && sclp_vt220_flush_later) sclp_vt220_emit_current(); /* Check if the tty needs a wake up call */ @@ -212,26 +221,7 @@ __sclp_vt220_emit(struct sclp_vt220_request *request) } /* - * Queue and emit given request. - */ -static void -sclp_vt220_emit(struct sclp_vt220_request *request) -{ - unsigned long flags; - int count; - - spin_lock_irqsave(&sclp_vt220_lock, flags); - list_add_tail(&request->list, &sclp_vt220_outqueue); - count = sclp_vt220_outqueue_count++; - spin_unlock_irqrestore(&sclp_vt220_lock, flags); - /* Emit only the first buffer immediately - callback takes care of - * the rest */ - if (count == 0 && __sclp_vt220_emit(request)) - sclp_vt220_process_queue(request); -} - -/* - * Queue and emit current request. Return zero on success, non-zero otherwise. + * Queue and emit current request. */ static void sclp_vt220_emit_current(void) @@ -241,22 +231,33 @@ sclp_vt220_emit_current(void) struct sclp_vt220_sccb *sccb; spin_lock_irqsave(&sclp_vt220_lock, flags); - request = NULL; - if (sclp_vt220_current_request != NULL) { + if (sclp_vt220_current_request) { sccb = (struct sclp_vt220_sccb *) sclp_vt220_current_request->sclp_req.sccb; /* Only emit buffers with content */ if (sccb->header.length != sizeof(struct sclp_vt220_sccb)) { - request = sclp_vt220_current_request; + list_add_tail(&sclp_vt220_current_request->list, + &sclp_vt220_outqueue); sclp_vt220_current_request = NULL; if (timer_pending(&sclp_vt220_timer)) del_timer(&sclp_vt220_timer); } sclp_vt220_flush_later = 0; } + if (sclp_vt220_queue_running || sclp_vt220_suspended) + goto out_unlock; + if (list_empty(&sclp_vt220_outqueue)) + goto out_unlock; + request = list_first_entry(&sclp_vt220_outqueue, + struct sclp_vt220_request, list); + sclp_vt220_queue_running = 1; + spin_unlock_irqrestore(&sclp_vt220_lock, flags); + + if (__sclp_vt220_emit(request)) + sclp_vt220_process_queue(request); + return; +out_unlock: spin_unlock_irqrestore(&sclp_vt220_lock, flags); - if (request != NULL) - sclp_vt220_emit(request); } #define SCLP_NORMAL_WRITE 0x00 @@ -396,7 +397,7 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, if (sclp_vt220_current_request == NULL) { while (list_empty(&sclp_vt220_empty)) { spin_unlock_irqrestore(&sclp_vt220_lock, flags); - if (may_fail) + if (may_fail || sclp_vt220_suspended) goto out; else sclp_sync_wait(); @@ -531,7 +532,7 @@ sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch) static void sclp_vt220_flush_chars(struct tty_struct *tty) { - if (sclp_vt220_outqueue_count == 0) + if (!sclp_vt220_queue_running) sclp_vt220_emit_current(); else sclp_vt220_flush_later = 1; @@ -635,7 +636,6 @@ static int __init __sclp_vt220_init(int num_pages) init_timer(&sclp_vt220_timer); sclp_vt220_current_request = NULL; sclp_vt220_buffered_chars = 0; - sclp_vt220_outqueue_count = 0; sclp_vt220_tty = NULL; sclp_vt220_flush_later = 0; @@ -736,7 +736,7 @@ static void __sclp_vt220_flush_buffer(void) spin_lock_irqsave(&sclp_vt220_lock, flags); if (timer_pending(&sclp_vt220_timer)) del_timer(&sclp_vt220_timer); - while (sclp_vt220_outqueue_count > 0) { + while (sclp_vt220_queue_running) { spin_unlock_irqrestore(&sclp_vt220_lock, flags); sclp_sync_wait(); spin_lock_irqsave(&sclp_vt220_lock, flags); @@ -744,6 +744,46 @@ static void __sclp_vt220_flush_buffer(void) spin_unlock_irqrestore(&sclp_vt220_lock, flags); } +/* + * Resume console: If there are cached messages, emit them. + */ +static void sclp_vt220_resume(void) +{ + unsigned long flags; + + spin_lock_irqsave(&sclp_vt220_lock, flags); + sclp_vt220_suspended = 0; + spin_unlock_irqrestore(&sclp_vt220_lock, flags); + sclp_vt220_emit_current(); +} + +/* + * Suspend console: Set suspend flag and flush console + */ +static void sclp_vt220_suspend(void) +{ + unsigned long flags; + + spin_lock_irqsave(&sclp_vt220_lock, flags); + sclp_vt220_suspended = 1; + spin_unlock_irqrestore(&sclp_vt220_lock, flags); + __sclp_vt220_flush_buffer(); +} + +static void sclp_vt220_pm_event_fn(struct sclp_register *reg, + enum sclp_pm_event sclp_pm_event) +{ + switch (sclp_pm_event) { + case SCLP_PM_EVENT_FREEZE: + sclp_vt220_suspend(); + break; + case SCLP_PM_EVENT_RESTORE: + case SCLP_PM_EVENT_THAW: + sclp_vt220_resume(); + break; + } +} + static int sclp_vt220_notify(struct notifier_block *self, unsigned long event, void *data) diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index 5469e099597..a2633377470 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -3,7 +3,7 @@ * tape device driver for 3480/3490E/3590 tapes. * * S390 and zSeries version - * Copyright IBM Corp. 2001,2006 + * Copyright IBM Corp. 2001, 2009 * Author(s): Carsten Otte <cotte@de.ibm.com> * Tuan Ngo-Anh <ngoanh@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> @@ -286,6 +286,7 @@ extern void tape_state_set(struct tape_device *, enum tape_state); extern int tape_generic_online(struct tape_device *, struct tape_discipline *); extern int tape_generic_offline(struct ccw_device *); +extern int tape_generic_pm_suspend(struct ccw_device *); /* Externals from tape_devmap.c */ extern int tape_generic_probe(struct ccw_device *); diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 2d00a383a47..5a519fac37b 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -2,7 +2,7 @@ * drivers/s390/char/tape_34xx.c * tape device discipline for 3480/3490 tapes. * - * Copyright (C) IBM Corp. 2001,2006 + * Copyright IBM Corp. 2001, 2009 * Author(s): Carsten Otte <cotte@de.ibm.com> * Tuan Ngo-Anh <ngoanh@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> @@ -1289,7 +1289,7 @@ static int tape_34xx_online(struct ccw_device *cdev) { return tape_generic_online( - cdev->dev.driver_data, + dev_get_drvdata(&cdev->dev), &tape_discipline_34xx ); } @@ -1302,6 +1302,7 @@ static struct ccw_driver tape_34xx_driver = { .remove = tape_generic_remove, .set_online = tape_34xx_online, .set_offline = tape_generic_offline, + .freeze = tape_generic_pm_suspend, }; static int diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index c453b2f3e9f..418f72dd39b 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -2,7 +2,7 @@ * drivers/s390/char/tape_3590.c * tape device discipline for 3590 tapes. * - * Copyright IBM Corp. 2001,2006 + * Copyright IBM Corp. 2001, 2009 * Author(s): Stefan Bader <shbader@de.ibm.com> * Michael Holzheu <holzheu@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> @@ -1703,7 +1703,7 @@ static struct ccw_device_id tape_3590_ids[] = { static int tape_3590_online(struct ccw_device *cdev) { - return tape_generic_online(cdev->dev.driver_data, + return tape_generic_online(dev_get_drvdata(&cdev->dev), &tape_discipline_3590); } @@ -1715,6 +1715,7 @@ static struct ccw_driver tape_3590_driver = { .remove = tape_generic_remove, .set_offline = tape_generic_offline, .set_online = tape_3590_online, + .freeze = tape_generic_pm_suspend, }; /* diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 8a109f3b69c..595aa04cfd0 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -3,7 +3,7 @@ * basic function of the tape device driver * * S390 and zSeries version - * Copyright IBM Corp. 2001,2006 + * Copyright IBM Corp. 2001, 2009 * Author(s): Carsten Otte <cotte@de.ibm.com> * Michael Holzheu <holzheu@de.ibm.com> * Tuan Ngo-Anh <ngoanh@de.ibm.com> @@ -92,7 +92,7 @@ tape_medium_state_show(struct device *dev, struct device_attribute *attr, char * { struct tape_device *tdev; - tdev = (struct tape_device *) dev->driver_data; + tdev = dev_get_drvdata(dev); return scnprintf(buf, PAGE_SIZE, "%i\n", tdev->medium_state); } @@ -104,7 +104,7 @@ tape_first_minor_show(struct device *dev, struct device_attribute *attr, char *b { struct tape_device *tdev; - tdev = (struct tape_device *) dev->driver_data; + tdev = dev_get_drvdata(dev); return scnprintf(buf, PAGE_SIZE, "%i\n", tdev->first_minor); } @@ -116,7 +116,7 @@ tape_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct tape_device *tdev; - tdev = (struct tape_device *) dev->driver_data; + tdev = dev_get_drvdata(dev); return scnprintf(buf, PAGE_SIZE, "%s\n", (tdev->first_minor < 0) ? "OFFLINE" : tape_state_verbose[tdev->tape_state]); } @@ -130,7 +130,7 @@ tape_operation_show(struct device *dev, struct device_attribute *attr, char *buf struct tape_device *tdev; ssize_t rc; - tdev = (struct tape_device *) dev->driver_data; + tdev = dev_get_drvdata(dev); if (tdev->first_minor < 0) return scnprintf(buf, PAGE_SIZE, "N/A\n"); @@ -156,7 +156,7 @@ tape_blocksize_show(struct device *dev, struct device_attribute *attr, char *buf { struct tape_device *tdev; - tdev = (struct tape_device *) dev->driver_data; + tdev = dev_get_drvdata(dev); return scnprintf(buf, PAGE_SIZE, "%i\n", tdev->char_data.block_size); } @@ -380,6 +380,55 @@ tape_cleanup_device(struct tape_device *device) } /* + * Suspend device. + * + * Called by the common I/O layer if the drive should be suspended on user + * request. We refuse to suspend if the device is loaded or in use for the + * following reason: + * While the Linux guest is suspended, it might be logged off which causes + * devices to be detached. Tape devices are automatically rewound and unloaded + * during DETACH processing (unless the tape device was attached with the + * NOASSIGN or MULTIUSER option). After rewind/unload, there is no way to + * resume the original state of the tape device, since we would need to + * manually re-load the cartridge which was active at suspend time. + */ +int tape_generic_pm_suspend(struct ccw_device *cdev) +{ + struct tape_device *device; + + device = cdev->dev.driver_data; + if (!device) { + return -ENODEV; + } + + DBF_LH(3, "(%08x): tape_generic_pm_suspend(%p)\n", + device->cdev_id, device); + + if (device->medium_state != MS_UNLOADED) { + pr_err("A cartridge is loaded in tape device %s, " + "refusing to suspend\n", dev_name(&cdev->dev)); + return -EBUSY; + } + + spin_lock_irq(get_ccwdev_lock(device->cdev)); + switch (device->tape_state) { + case TS_INIT: + case TS_NOT_OPER: + case TS_UNUSED: + spin_unlock_irq(get_ccwdev_lock(device->cdev)); + break; + default: + pr_err("Tape device %s is busy, refusing to " + "suspend\n", dev_name(&cdev->dev)); + spin_unlock_irq(get_ccwdev_lock(device->cdev)); + return -EBUSY; + } + + DBF_LH(3, "(%08x): Drive suspended.\n", device->cdev_id); + return 0; +} + +/* * Set device offline. * * Called by the common I/O layer if the drive should set offline on user @@ -391,7 +440,7 @@ tape_generic_offline(struct ccw_device *cdev) { struct tape_device *device; - device = cdev->dev.driver_data; + device = dev_get_drvdata(&cdev->dev); if (!device) { return -ENODEV; } @@ -534,7 +583,7 @@ tape_generic_probe(struct ccw_device *cdev) tape_put_device(device); return ret; } - cdev->dev.driver_data = device; + dev_set_drvdata(&cdev->dev, device); cdev->handler = __tape_do_irq; device->cdev = cdev; ccw_device_get_id(cdev, &dev_id); @@ -573,7 +622,7 @@ tape_generic_remove(struct ccw_device *cdev) { struct tape_device * device; - device = cdev->dev.driver_data; + device = dev_get_drvdata(&cdev->dev); if (!device) { return; } @@ -613,9 +662,9 @@ tape_generic_remove(struct ccw_device *cdev) tape_cleanup_device(device); } - if (cdev->dev.driver_data != NULL) { + if (!dev_get_drvdata(&cdev->dev)) { sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group); - cdev->dev.driver_data = tape_put_device(cdev->dev.driver_data); + dev_set_drvdata(&cdev->dev, tape_put_device(dev_get_drvdata(&cdev->dev))); } } @@ -1011,7 +1060,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) struct tape_request *request; int rc; - device = (struct tape_device *) cdev->dev.driver_data; + device = dev_get_drvdata(&cdev->dev); if (device == NULL) { return; } @@ -1273,6 +1322,7 @@ EXPORT_SYMBOL(tape_generic_remove); EXPORT_SYMBOL(tape_generic_probe); 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_state_verbose); diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index d8a2289fcb6..411cfa3c771 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -3,7 +3,7 @@ * character device driver for reading z/VM system service records * * - * Copyright 2004 IBM Corporation + * Copyright IBM Corp. 2004, 2009 * character device driver for reading z/VM system service records, * Version 1.0 * Author(s): Xenia Tkatschow <xenia@us.ibm.com> @@ -504,7 +504,7 @@ static ssize_t vmlogrdr_autopurge_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t count) { - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); ssize_t ret = count; switch (buf[0]) { @@ -525,7 +525,7 @@ static ssize_t vmlogrdr_autopurge_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); return sprintf(buf, "%u\n", priv->autopurge); } @@ -541,7 +541,7 @@ static ssize_t vmlogrdr_purge_store(struct device * dev, char cp_command[80]; char cp_response[80]; - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); if (buf[0] != '1') return -EINVAL; @@ -578,7 +578,7 @@ static ssize_t vmlogrdr_autorecording_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); ssize_t ret = count; switch (buf[0]) { @@ -599,7 +599,7 @@ static ssize_t vmlogrdr_autorecording_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); return sprintf(buf, "%u\n", priv->autorecording); } @@ -612,7 +612,7 @@ static ssize_t vmlogrdr_recording_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t count) { - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); ssize_t ret; switch (buf[0]) { @@ -660,6 +660,29 @@ static struct attribute *vmlogrdr_attrs[] = { NULL, }; +static int vmlogrdr_pm_prepare(struct device *dev) +{ + int rc; + struct vmlogrdr_priv_t *priv = dev->driver_data; + + rc = 0; + if (priv) { + spin_lock_bh(&priv->priv_lock); + if (priv->dev_in_use) + rc = -EBUSY; + spin_unlock_bh(&priv->priv_lock); + } + if (rc) + pr_err("vmlogrdr: device %s is busy. Refuse to suspend.\n", + dev_name(dev)); + return rc; +} + + +static struct dev_pm_ops vmlogrdr_pm_ops = { + .prepare = vmlogrdr_pm_prepare, +}; + static struct attribute_group vmlogrdr_attr_group = { .attrs = vmlogrdr_attrs, }; @@ -668,6 +691,7 @@ static struct class *vmlogrdr_class; static struct device_driver vmlogrdr_driver = { .name = "vmlogrdr", .bus = &iucv_bus, + .pm = &vmlogrdr_pm_ops, }; @@ -729,6 +753,7 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) dev->bus = &iucv_bus; dev->parent = iucv_root; dev->driver = &vmlogrdr_driver; + dev->driver_data = priv; /* * The release function could be called after the * module has been unloaded. It's _only_ task is to diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 5dcef81fc9d..7d9e67cb647 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -2,7 +2,7 @@ * Linux driver for System z and s390 unit record devices * (z/VM virtual punch, reader, printer) * - * Copyright IBM Corp. 2001, 2007 + * Copyright IBM Corp. 2001, 2009 * Authors: Malcolm Beattie <beattiem@uk.ibm.com> * Michael Holzheu <holzheu@de.ibm.com> * Frank Munzert <munzert@de.ibm.com> @@ -60,6 +60,7 @@ static int ur_probe(struct ccw_device *cdev); static void ur_remove(struct ccw_device *cdev); static int ur_set_online(struct ccw_device *cdev); static int ur_set_offline(struct ccw_device *cdev); +static int ur_pm_suspend(struct ccw_device *cdev); static struct ccw_driver ur_driver = { .name = "vmur", @@ -69,6 +70,7 @@ static struct ccw_driver ur_driver = { .remove = ur_remove, .set_online = ur_set_online, .set_offline = ur_set_offline, + .freeze = ur_pm_suspend, }; static DEFINE_MUTEX(vmur_mutex); @@ -78,11 +80,11 @@ static DEFINE_MUTEX(vmur_mutex); * * Each ur device (urd) contains a reference to its corresponding ccw device * (cdev) using the urd->cdev pointer. Each ccw device has a reference to the - * ur device using the cdev->dev.driver_data pointer. + * ur device using dev_get_drvdata(&cdev->dev) pointer. * * urd references: * - ur_probe gets a urd reference, ur_remove drops the reference - * (cdev->dev.driver_data) + * dev_get_drvdata(&cdev->dev) * - ur_open gets a urd reference, ur_relase drops the reference * (urf->urd) * @@ -90,7 +92,7 @@ static DEFINE_MUTEX(vmur_mutex); * - urdev_alloc get a cdev reference (urd->cdev) * - urdev_free drops the cdev reference (urd->cdev) * - * Setting and clearing of cdev->dev.driver_data is protected by the ccwdev lock + * Setting and clearing of dev_get_drvdata(&cdev->dev) is protected by the ccwdev lock */ static struct urdev *urdev_alloc(struct ccw_device *cdev) { @@ -129,7 +131,7 @@ static struct urdev *urdev_get_from_cdev(struct ccw_device *cdev) unsigned long flags; spin_lock_irqsave(get_ccwdev_lock(cdev), flags); - urd = cdev->dev.driver_data; + urd = dev_get_drvdata(&cdev->dev); if (urd) urdev_get(urd); spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); @@ -158,6 +160,28 @@ static void urdev_put(struct urdev *urd) } /* + * State and contents of ur devices can be changed by class D users issuing + * CP commands such as PURGE or TRANSFER, while the Linux guest is suspended. + * Also the Linux guest might be logged off, which causes all active spool + * files to be closed. + * So we cannot guarantee that spool files are still the same when the Linux + * guest is resumed. In order to avoid unpredictable results at resume time + * we simply refuse to suspend if a ur device node is open. + */ +static int ur_pm_suspend(struct ccw_device *cdev) +{ + struct urdev *urd = cdev->dev.driver_data; + + TRACE("ur_pm_suspend: cdev=%p\n", cdev); + if (urd->open_flag) { + pr_err("Unit record device %s is busy, %s refusing to " + "suspend.\n", dev_name(&cdev->dev), ur_banner); + return -EBUSY; + } + return 0; +} + +/* * Low-level functions to do I/O to a ur device. * alloc_chan_prog * free_chan_prog @@ -286,7 +310,7 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm, TRACE("ur_int_handler: unsolicited interrupt\n"); return; } - urd = cdev->dev.driver_data; + urd = dev_get_drvdata(&cdev->dev); BUG_ON(!urd); /* On special conditions irb is an error pointer */ if (IS_ERR(irb)) @@ -832,7 +856,7 @@ static int ur_probe(struct ccw_device *cdev) goto fail_remove_attr; } spin_lock_irq(get_ccwdev_lock(cdev)); - cdev->dev.driver_data = urd; + dev_set_drvdata(&cdev->dev, urd); spin_unlock_irq(get_ccwdev_lock(cdev)); mutex_unlock(&vmur_mutex); @@ -972,8 +996,8 @@ static void ur_remove(struct ccw_device *cdev) ur_remove_attributes(&cdev->dev); spin_lock_irqsave(get_ccwdev_lock(cdev), flags); - urdev_put(cdev->dev.driver_data); - cdev->dev.driver_data = NULL; + urdev_put(dev_get_drvdata(&cdev->dev)); + dev_set_drvdata(&cdev->dev, NULL); spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); mutex_unlock(&vmur_mutex); diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index 21a2a829bf4..cb7854c10c0 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -1,17 +1,23 @@ /* * Watchdog implementation based on z/VM Watchdog Timer API * + * Copyright IBM Corp. 2004,2009 + * * The user space watchdog daemon can use this driver as * /dev/vmwatchdog to have z/VM execute the specified CP * command when the timeout expires. The default command is * "IPL", which which cause an immediate reboot. */ +#define KMSG_COMPONENT "vmwatchdog" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/init.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/suspend.h> #include <linux/watchdog.h> #include <linux/smp_lock.h> @@ -43,6 +49,9 @@ static unsigned int vmwdt_interval = 60; static unsigned long vmwdt_is_open; static int vmwdt_expect_close; +#define VMWDT_OPEN 0 /* devnode is open or suspend in progress */ +#define VMWDT_RUNNING 1 /* The watchdog is armed */ + enum vmwdt_func { /* function codes */ wdt_init = 0, @@ -92,6 +101,7 @@ static int vmwdt_keepalive(void) EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init; + set_bit(VMWDT_RUNNING, &vmwdt_is_open); ret = __diag288(func, vmwdt_interval, ebc_cmd, len); WARN_ON(ret != 0); kfree(ebc_cmd); @@ -102,6 +112,7 @@ static int vmwdt_disable(void) { int ret = __diag288(wdt_cancel, 0, "", 0); WARN_ON(ret != 0); + clear_bit(VMWDT_RUNNING, &vmwdt_is_open); return ret; } @@ -123,13 +134,13 @@ static int vmwdt_open(struct inode *i, struct file *f) { int ret; lock_kernel(); - if (test_and_set_bit(0, &vmwdt_is_open)) { + if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { unlock_kernel(); return -EBUSY; } ret = vmwdt_keepalive(); if (ret) - clear_bit(0, &vmwdt_is_open); + clear_bit(VMWDT_OPEN, &vmwdt_is_open); unlock_kernel(); return ret ? ret : nonseekable_open(i, f); } @@ -139,7 +150,7 @@ static int vmwdt_close(struct inode *i, struct file *f) if (vmwdt_expect_close == 42) vmwdt_disable(); vmwdt_expect_close = 0; - clear_bit(0, &vmwdt_is_open); + clear_bit(VMWDT_OPEN, &vmwdt_is_open); return 0; } @@ -223,6 +234,57 @@ static ssize_t vmwdt_write(struct file *f, const char __user *buf, return count; } +static int vmwdt_resume(void) +{ + clear_bit(VMWDT_OPEN, &vmwdt_is_open); + return NOTIFY_DONE; +} + +/* + * It makes no sense to go into suspend while the watchdog is running. + * Depending on the memory size, the watchdog might trigger, while we + * are still saving the memory. + * We reuse the open flag to ensure that suspend and watchdog open are + * exclusive operations + */ +static int vmwdt_suspend(void) +{ + if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { + pr_err("The watchdog is in use. " + "This prevents hibernation or suspend.\n"); + return NOTIFY_BAD; + } + if (test_bit(VMWDT_RUNNING, &vmwdt_is_open)) { + clear_bit(VMWDT_OPEN, &vmwdt_is_open); + pr_err("The watchdog is running. " + "This prevents hibernation or suspend.\n"); + return NOTIFY_BAD; + } + return NOTIFY_DONE; +} + +/* + * This function is called for suspend and resume. + */ +static int vmwdt_power_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + switch (event) { + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + return vmwdt_resume(); + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + return vmwdt_suspend(); + default: + return NOTIFY_DONE; + } +} + +static struct notifier_block vmwdt_power_notifier = { + .notifier_call = vmwdt_power_event, +}; + static const struct file_operations vmwdt_fops = { .open = &vmwdt_open, .release = &vmwdt_close, @@ -244,12 +306,21 @@ static int __init vmwdt_init(void) ret = vmwdt_probe(); if (ret) return ret; - return misc_register(&vmwdt_dev); + ret = register_pm_notifier(&vmwdt_power_notifier); + if (ret) + return ret; + ret = misc_register(&vmwdt_dev); + if (ret) { + unregister_pm_notifier(&vmwdt_power_notifier); + return ret; + } + return 0; } module_init(vmwdt_init); static void __exit vmwdt_exit(void) { - WARN_ON(misc_deregister(&vmwdt_dev) != 0); + unregister_pm_notifier(&vmwdt_power_notifier); + misc_deregister(&vmwdt_dev); } module_exit(vmwdt_exit); diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 22ce765d537..a5a62f1f774 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -1,11 +1,10 @@ /* - * drivers/s390/cio/ccwgroup.c * bus driver for ccwgroup * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Arnd Bergmann (arndb@de.ibm.com) - * Cornelia Huck (cornelia.huck@de.ibm.com) + * Copyright IBM Corp. 2002, 2009 + * + * Author(s): Arnd Bergmann (arndb@de.ibm.com) + * Cornelia Huck (cornelia.huck@de.ibm.com) */ #include <linux/module.h> #include <linux/errno.h> @@ -501,6 +500,74 @@ static void ccwgroup_shutdown(struct device *dev) gdrv->shutdown(gdev); } +static int ccwgroup_pm_prepare(struct device *dev) +{ + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver); + + /* Fail while device is being set online/offline. */ + if (atomic_read(&gdev->onoff)) + return -EAGAIN; + + if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE) + return 0; + + return gdrv->prepare ? gdrv->prepare(gdev) : 0; +} + +static void ccwgroup_pm_complete(struct device *dev) +{ + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver); + + if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE) + return; + + if (gdrv->complete) + gdrv->complete(gdev); +} + +static int ccwgroup_pm_freeze(struct device *dev) +{ + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver); + + if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE) + return 0; + + return gdrv->freeze ? gdrv->freeze(gdev) : 0; +} + +static int ccwgroup_pm_thaw(struct device *dev) +{ + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver); + + if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE) + return 0; + + return gdrv->thaw ? gdrv->thaw(gdev) : 0; +} + +static int ccwgroup_pm_restore(struct device *dev) +{ + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver); + + if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE) + return 0; + + return gdrv->restore ? gdrv->restore(gdev) : 0; +} + +static struct dev_pm_ops ccwgroup_pm_ops = { + .prepare = ccwgroup_pm_prepare, + .complete = ccwgroup_pm_complete, + .freeze = ccwgroup_pm_freeze, + .thaw = ccwgroup_pm_thaw, + .restore = ccwgroup_pm_restore, +}; + static struct bus_type ccwgroup_bus_type = { .name = "ccwgroup", .match = ccwgroup_bus_match, @@ -508,6 +575,7 @@ static struct bus_type ccwgroup_bus_type = { .probe = ccwgroup_probe, .remove = ccwgroup_remove, .shutdown = ccwgroup_shutdown, + .pm = &ccwgroup_pm_ops, }; diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 883f16f96f2..1ecd3e56764 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -549,8 +549,7 @@ cleanup: return ret; } -static int -__chsc_do_secm(struct channel_subsystem *css, int enable, void *page) +int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) { struct { struct chsc_header request; diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index ba59bceace9..425e8f89a6c 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -90,6 +90,7 @@ extern void chsc_free_sei_area(void); extern int chsc_enable_facility(int); struct channel_subsystem; extern int chsc_secm(struct channel_subsystem *, int); +int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page); int chsc_chp_vary(struct chp_id chpid, int on); int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c index 93eca1731b8..cc5144b6f9d 100644 --- a/drivers/s390/cio/chsc_sch.c +++ b/drivers/s390/cio/chsc_sch.c @@ -1,7 +1,8 @@ /* * Driver for s390 chsc subchannels * - * Copyright IBM Corp. 2008 + * Copyright IBM Corp. 2008, 2009 + * * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> * */ @@ -112,6 +113,31 @@ static void chsc_subchannel_shutdown(struct subchannel *sch) cio_disable_subchannel(sch); } +static int chsc_subchannel_prepare(struct subchannel *sch) +{ + int cc; + struct schib schib; + /* + * Don't allow suspend while the subchannel is not idle + * since we don't have a way to clear the subchannel and + * cannot disable it with a request running. + */ + cc = stsch(sch->schid, &schib); + if (!cc && scsw_stctl(&schib.scsw)) + return -EAGAIN; + return 0; +} + +static int chsc_subchannel_freeze(struct subchannel *sch) +{ + return cio_disable_subchannel(sch); +} + +static int chsc_subchannel_restore(struct subchannel *sch) +{ + return cio_enable_subchannel(sch, (u32)(unsigned long)sch); +} + static struct css_device_id chsc_subchannel_ids[] = { { .match_flags = 0x1, .type =SUBCHANNEL_TYPE_CHSC, }, { /* end of list */ }, @@ -125,6 +151,10 @@ static struct css_driver chsc_subchannel_driver = { .probe = chsc_subchannel_probe, .remove = chsc_subchannel_remove, .shutdown = chsc_subchannel_shutdown, + .prepare = chsc_subchannel_prepare, + .freeze = chsc_subchannel_freeze, + .thaw = chsc_subchannel_restore, + .restore = chsc_subchannel_restore, .name = "chsc_subchannel", }; diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index dc98b2c6386..30f51611130 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -1204,6 +1204,11 @@ static ssize_t cmb_enable_store(struct device *dev, DEVICE_ATTR(cmb_enable, 0644, cmb_enable_show, cmb_enable_store); +int ccw_set_cmf(struct ccw_device *cdev, int enable) +{ + return cmbops->set(cdev, enable ? 2 : 0); +} + /** * enable_cmf() - switch on the channel measurement for a specific device * @cdev: The ccw device to be enabled diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 0085d890179..85d43c6bcb6 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -1,10 +1,10 @@ /* - * drivers/s390/cio/css.c - * driver for channel subsystem + * driver for channel subsystem * - * Copyright IBM Corp. 2002,2008 - * Author(s): Arnd Bergmann (arndb@de.ibm.com) - * Cornelia Huck (cornelia.huck@de.ibm.com) + * Copyright IBM Corp. 2002, 2009 + * + * Author(s): Arnd Bergmann (arndb@de.ibm.com) + * Cornelia Huck (cornelia.huck@de.ibm.com) */ #define KMSG_COMPONENT "cio" @@ -17,6 +17,7 @@ #include <linux/errno.h> #include <linux/list.h> #include <linux/reboot.h> +#include <linux/suspend.h> #include <asm/isc.h> #include <asm/crw.h> @@ -780,6 +781,79 @@ static struct notifier_block css_reboot_notifier = { }; /* + * Since the css devices are neither on a bus nor have a class + * nor have a special device type, we cannot stop/restart channel + * path measurements via the normal suspend/resume callbacks, but have + * to use notifiers. + */ +static int css_power_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + void *secm_area; + int ret, i; + + switch (event) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + ret = NOTIFY_DONE; + for (i = 0; i <= __MAX_CSSID; i++) { + struct channel_subsystem *css; + + css = channel_subsystems[i]; + mutex_lock(&css->mutex); + if (!css->cm_enabled) { + mutex_unlock(&css->mutex); + continue; + } + secm_area = (void *)get_zeroed_page(GFP_KERNEL | + GFP_DMA); + if (secm_area) { + if (__chsc_do_secm(css, 0, secm_area)) + ret = NOTIFY_BAD; + free_page((unsigned long)secm_area); + } else + ret = NOTIFY_BAD; + + mutex_unlock(&css->mutex); + } + break; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + ret = NOTIFY_DONE; + for (i = 0; i <= __MAX_CSSID; i++) { + struct channel_subsystem *css; + + css = channel_subsystems[i]; + mutex_lock(&css->mutex); + if (!css->cm_enabled) { + mutex_unlock(&css->mutex); + continue; + } + secm_area = (void *)get_zeroed_page(GFP_KERNEL | + GFP_DMA); + if (secm_area) { + if (__chsc_do_secm(css, 1, secm_area)) + ret = NOTIFY_BAD; + free_page((unsigned long)secm_area); + } else + ret = NOTIFY_BAD; + + mutex_unlock(&css->mutex); + } + /* search for subchannels, which appeared during hibernation */ + css_schedule_reprobe(); + break; + default: + ret = NOTIFY_DONE; + } + return ret; + +} +static struct notifier_block css_power_notifier = { + .notifier_call = css_power_event, +}; + +/* * Now that the driver core is running, we can setup our channel subsystem. * The struct subchannel's are created during probing (except for the * static console subchannel). @@ -852,6 +926,11 @@ init_channel_subsystem (void) ret = register_reboot_notifier(&css_reboot_notifier); if (ret) goto out_unregister; + ret = register_pm_notifier(&css_power_notifier); + if (ret) { + unregister_reboot_notifier(&css_reboot_notifier); + goto out_unregister; + } css_init_done = 1; /* Enable default isc for I/O subchannels. */ @@ -953,6 +1032,73 @@ static int css_uevent(struct device *dev, struct kobj_uevent_env *env) return ret; } +static int css_pm_prepare(struct device *dev) +{ + struct subchannel *sch = to_subchannel(dev); + struct css_driver *drv; + + if (mutex_is_locked(&sch->reg_mutex)) + return -EAGAIN; + if (!sch->dev.driver) + return 0; + drv = to_cssdriver(sch->dev.driver); + /* Notify drivers that they may not register children. */ + return drv->prepare ? drv->prepare(sch) : 0; +} + +static void css_pm_complete(struct device *dev) +{ + struct subchannel *sch = to_subchannel(dev); + struct css_driver *drv; + + if (!sch->dev.driver) + return; + drv = to_cssdriver(sch->dev.driver); + if (drv->complete) + drv->complete(sch); +} + +static int css_pm_freeze(struct device *dev) +{ + struct subchannel *sch = to_subchannel(dev); + struct css_driver *drv; + + if (!sch->dev.driver) + return 0; + drv = to_cssdriver(sch->dev.driver); + return drv->freeze ? drv->freeze(sch) : 0; +} + +static int css_pm_thaw(struct device *dev) +{ + struct subchannel *sch = to_subchannel(dev); + struct css_driver *drv; + + if (!sch->dev.driver) + return 0; + drv = to_cssdriver(sch->dev.driver); + return drv->thaw ? drv->thaw(sch) : 0; +} + +static int css_pm_restore(struct device *dev) +{ + struct subchannel *sch = to_subchannel(dev); + struct css_driver *drv; + + if (!sch->dev.driver) + return 0; + drv = to_cssdriver(sch->dev.driver); + return drv->restore ? drv->restore(sch) : 0; +} + +static struct dev_pm_ops css_pm_ops = { + .prepare = css_pm_prepare, + .complete = css_pm_complete, + .freeze = css_pm_freeze, + .thaw = css_pm_thaw, + .restore = css_pm_restore, +}; + struct bus_type css_bus_type = { .name = "css", .match = css_bus_match, @@ -960,6 +1106,7 @@ struct bus_type css_bus_type = { .remove = css_remove, .shutdown = css_shutdown, .uevent = css_uevent, + .pm = &css_pm_ops, }; /** diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 57ebf120f82..9763eeec745 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -70,6 +70,11 @@ struct chp_link; * @probe: function called on probe * @remove: function called on remove * @shutdown: called at device shutdown + * @prepare: prepare for pm state transition + * @complete: undo work done in @prepare + * @freeze: callback for freezing during hibernation snapshotting + * @thaw: undo work done in @freeze + * @restore: callback for restoring after hibernation * @name: name of the device driver */ struct css_driver { @@ -82,6 +87,11 @@ struct css_driver { int (*probe)(struct subchannel *); int (*remove)(struct subchannel *); void (*shutdown)(struct subchannel *); + int (*prepare) (struct subchannel *); + void (*complete) (struct subchannel *); + int (*freeze)(struct subchannel *); + int (*thaw) (struct subchannel *); + int (*restore)(struct subchannel *); const char *name; }; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 35441fa16be..3c57c1a18bb 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -138,6 +138,19 @@ static struct css_device_id io_subchannel_ids[] = { }; MODULE_DEVICE_TABLE(css, io_subchannel_ids); +static int io_subchannel_prepare(struct subchannel *sch) +{ + struct ccw_device *cdev; + /* + * Don't allow suspend while a ccw device registration + * is still outstanding. + */ + cdev = sch_get_cdev(sch); + if (cdev && !device_is_registered(&cdev->dev)) + return -EAGAIN; + return 0; +} + static struct css_driver io_subchannel_driver = { .owner = THIS_MODULE, .subchannel_type = io_subchannel_ids, @@ -148,6 +161,7 @@ static struct css_driver io_subchannel_driver = { .probe = io_subchannel_probe, .remove = io_subchannel_remove, .shutdown = io_subchannel_shutdown, + .prepare = io_subchannel_prepare, }; struct workqueue_struct *ccw_device_work; @@ -1775,6 +1789,15 @@ ccw_device_probe_console(void) return &console_cdev; } +static int ccw_device_pm_restore(struct device *dev); + +int ccw_device_force_console(void) +{ + if (!console_cdev_in_use) + return -ENODEV; + return ccw_device_pm_restore(&console_cdev.dev); +} +EXPORT_SYMBOL_GPL(ccw_device_force_console); const char *cio_get_console_cdev_name(struct subchannel *sch) { @@ -1895,6 +1918,242 @@ static void ccw_device_shutdown(struct device *dev) disable_cmf(cdev); } +static int ccw_device_pm_prepare(struct device *dev) +{ + struct ccw_device *cdev = to_ccwdev(dev); + + if (work_pending(&cdev->private->kick_work)) + return -EAGAIN; + /* Fail while device is being set online/offline. */ + if (atomic_read(&cdev->private->onoff)) + return -EAGAIN; + + if (cdev->online && cdev->drv && cdev->drv->prepare) + return cdev->drv->prepare(cdev); + + return 0; +} + +static void ccw_device_pm_complete(struct device *dev) +{ + struct ccw_device *cdev = to_ccwdev(dev); + + if (cdev->online && cdev->drv && cdev->drv->complete) + cdev->drv->complete(cdev); +} + +static int ccw_device_pm_freeze(struct device *dev) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct subchannel *sch = to_subchannel(cdev->dev.parent); + int ret, cm_enabled; + + /* Fail suspend while device is in transistional state. */ + if (!dev_fsm_final_state(cdev)) + return -EAGAIN; + if (!cdev->online) + return 0; + if (cdev->drv && cdev->drv->freeze) { + ret = cdev->drv->freeze(cdev); + if (ret) + return ret; + } + + spin_lock_irq(sch->lock); + cm_enabled = cdev->private->cmb != NULL; + spin_unlock_irq(sch->lock); + if (cm_enabled) { + /* Don't have the css write on memory. */ + ret = ccw_set_cmf(cdev, 0); + if (ret) + return ret; + } + /* From here on, disallow device driver I/O. */ + spin_lock_irq(sch->lock); + ret = cio_disable_subchannel(sch); + spin_unlock_irq(sch->lock); + + return ret; +} + +static int ccw_device_pm_thaw(struct device *dev) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct subchannel *sch = to_subchannel(cdev->dev.parent); + int ret, cm_enabled; + + if (!cdev->online) + return 0; + + spin_lock_irq(sch->lock); + /* Allow device driver I/O again. */ + ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); + cm_enabled = cdev->private->cmb != NULL; + spin_unlock_irq(sch->lock); + if (ret) + return ret; + + if (cm_enabled) { + ret = ccw_set_cmf(cdev, 1); + if (ret) + return ret; + } + + if (cdev->drv && cdev->drv->thaw) + ret = cdev->drv->thaw(cdev); + + return ret; +} + +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; + /* + * While we were sleeping, devices may have gone or become + * available again. Kick re-detection. + */ + spin_lock_irq(sch->lock); + cdev->private->flags.resuming = 1; + ret = ccw_device_recognition(cdev); + spin_unlock_irq(sch->lock); + if (ret) { + CIO_MSG_EVENT(0, "Couldn't start recognition for device " + "%s (ret=%d)\n", dev_name(&cdev->dev), 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; +} + +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); + return -ENODEV; +} + +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); + return -ENODEV; +} + +static int ccw_device_pm_restore(struct device *dev) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct subchannel *sch = to_subchannel(cdev->dev.parent); + int ret = 0, cm_enabled; + + __ccw_device_pm_restore(cdev); + spin_lock_irq(sch->lock); + if (cio_is_console(sch->schid)) { + cio_enable_subchannel(sch, (u32)(addr_t)sch); + spin_unlock_irq(sch->lock); + goto out_restore; + } + cdev->private->flags.donotify = 0; + /* check recognition results */ + switch (cdev->private->state) { + case DEV_STATE_OFFLINE: + break; + case DEV_STATE_BOXED: + ret = resume_handle_boxed(cdev); + spin_unlock_irq(sch->lock); + if (ret) + goto out; + goto out_restore; + case DEV_STATE_DISCONNECTED: + goto out_disc_unlock; + default: + goto out_unreg_unlock; + } + /* check if the device id has changed */ + if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { + CIO_MSG_EVENT(0, "resume: sch %s: failed (devno changed from " + "%04x to %04x)\n", dev_name(&sch->dev), + cdev->private->dev_id.devno, + sch->schib.pmcw.dev); + goto out_unreg_unlock; + } + /* 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); + ret = -ENODEV; + goto out_unlock; + } + if (!cdev->online) { + ret = 0; + goto out_unlock; + } + ret = ccw_device_online(cdev); + if (ret) + goto out_disc_unlock; + + cm_enabled = cdev->private->cmb != NULL; + spin_unlock_irq(sch->lock); + + wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); + if (cdev->private->state != DEV_STATE_ONLINE) { + spin_lock_irq(sch->lock); + goto out_disc_unlock; + } + if (cm_enabled) { + ret = ccw_set_cmf(cdev, 1); + if (ret) { + CIO_MSG_EVENT(2, "resume: cdev %s: cmf failed " + "(rc=%d)\n", dev_name(&cdev->dev), ret); + ret = 0; + } + } + +out_restore: + if (cdev->online && cdev->drv && cdev->drv->restore) + ret = cdev->drv->restore(cdev); +out: + return ret; + +out_disc_unlock: + ret = resume_handle_disc(cdev); + spin_unlock_irq(sch->lock); + if (ret) + return ret; + goto out_restore; + +out_unreg_unlock: + ccw_device_schedule_sch_unregister(cdev); + ret = -ENODEV; +out_unlock: + spin_unlock_irq(sch->lock); + return ret; +} + +static struct dev_pm_ops ccw_pm_ops = { + .prepare = ccw_device_pm_prepare, + .complete = ccw_device_pm_complete, + .freeze = ccw_device_pm_freeze, + .thaw = ccw_device_pm_thaw, + .restore = ccw_device_pm_restore, +}; + struct bus_type ccw_bus_type = { .name = "ccw", .match = ccw_bus_match, @@ -1902,6 +2161,7 @@ struct bus_type ccw_bus_type = { .probe = ccw_device_probe, .remove = ccw_device_remove, .shutdown = ccw_device_shutdown, + .pm = &ccw_pm_ops, }; /** diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index f1cbbd94ad4..e3975107a57 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -87,6 +87,8 @@ int ccw_device_is_orphan(struct ccw_device *); int 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); @@ -133,5 +135,6 @@ extern struct bus_type ccw_bus_type; void retry_set_schib(struct ccw_device *cdev); void cmf_retry_copy_block(struct ccw_device *); int cmf_reenable(struct ccw_device *); +int ccw_set_cmf(struct ccw_device *cdev, int enable); extern struct device_attribute dev_attr_cmb_enable; #endif diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index e4604926156..3db88c52d28 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -177,29 +177,21 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev) panic("Can't stop i/o on subchannel.\n"); } -static int -ccw_device_handle_oper(struct ccw_device *cdev) +void ccw_device_update_sense_data(struct ccw_device *cdev) { - struct subchannel *sch; + memset(&cdev->id, 0, sizeof(cdev->id)); + cdev->id.cu_type = cdev->private->senseid.cu_type; + cdev->id.cu_model = cdev->private->senseid.cu_model; + cdev->id.dev_type = cdev->private->senseid.dev_type; + cdev->id.dev_model = cdev->private->senseid.dev_model; +} - sch = to_subchannel(cdev->dev.parent); - cdev->private->flags.recog_done = 1; - /* - * Check if cu type and device type still match. If - * not, it is certainly another device and we have to - * de- and re-register. - */ - if (cdev->id.cu_type != cdev->private->senseid.cu_type || - cdev->id.cu_model != cdev->private->senseid.cu_model || - cdev->id.dev_type != cdev->private->senseid.dev_type || - cdev->id.dev_model != cdev->private->senseid.dev_model) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_do_unbind_bind); - queue_work(ccw_device_work, &cdev->private->kick_work); - return 0; - } - cdev->private->flags.donotify = 1; - return 1; +int ccw_device_test_sense_data(struct ccw_device *cdev) +{ + return cdev->id.cu_type == cdev->private->senseid.cu_type && + cdev->id.cu_model == cdev->private->senseid.cu_model && + cdev->id.dev_type == cdev->private->senseid.dev_type && + cdev->id.dev_model == cdev->private->senseid.dev_model; } /* @@ -233,7 +225,7 @@ static void ccw_device_recog_done(struct ccw_device *cdev, int state) { struct subchannel *sch; - int notify, old_lpm, same_dev; + int old_lpm; sch = to_subchannel(cdev->dev.parent); @@ -263,8 +255,12 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) wake_up(&cdev->private->wait_q); return; } - notify = 0; - same_dev = 0; /* Keep the compiler quiet... */ + if (cdev->private->flags.resuming) { + cdev->private->state = state; + cdev->private->flags.recog_done = 1; + wake_up(&cdev->private->wait_q); + return; + } switch (state) { case DEV_STATE_NOT_OPER: CIO_MSG_EVENT(2, "SenseID : unknown device %04x on " @@ -273,34 +269,31 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) sch->schid.ssid, sch->schid.sch_no); break; case DEV_STATE_OFFLINE: - if (cdev->online) { - same_dev = ccw_device_handle_oper(cdev); - notify = 1; + 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; } - /* fill out sense information */ - memset(&cdev->id, 0, sizeof(cdev->id)); - cdev->id.cu_type = cdev->private->senseid.cu_type; - cdev->id.cu_model = cdev->private->senseid.cu_model; - cdev->id.dev_type = cdev->private->senseid.dev_type; - cdev->id.dev_model = cdev->private->senseid.dev_model; - if (notify) { - cdev->private->state = DEV_STATE_OFFLINE; - if (same_dev) { - /* Get device online again. */ - ccw_device_online(cdev); - wake_up(&cdev->private->wait_q); - } - return; + cdev->private->state = DEV_STATE_OFFLINE; + cdev->private->flags.recog_done = 1; + if (ccw_device_test_sense_data(cdev)) { + cdev->private->flags.donotify = 1; + ccw_device_online(cdev); + 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); } - /* 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; + return; case DEV_STATE_BOXED: CIO_MSG_EVENT(0, "SenseID : boxed device %04x on " " subchannel 0.%x.%04x\n", @@ -502,9 +495,6 @@ ccw_device_recognition(struct ccw_device *cdev) struct subchannel *sch; int ret; - if ((cdev->private->state != DEV_STATE_NOT_OPER) && - (cdev->private->state != DEV_STATE_BOXED)) - return -EINVAL; sch = to_subchannel(cdev->dev.parent); ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); if (ret != 0) diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index bf0a24af39a..2d0efee8a29 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -1,10 +1,8 @@ /* - * drivers/s390/cio/device_ops.c + * Copyright IBM Corp. 2002, 2009 * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) - * Cornelia Huck (cornelia.huck@de.ibm.com) + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) + * Cornelia Huck (cornelia.huck@de.ibm.com) */ #include <linux/module.h> #include <linux/init.h> @@ -116,12 +114,15 @@ int ccw_device_clear(struct ccw_device *cdev, unsigned long intparm) if (!cdev || !cdev->dev.parent) return -ENODEV; + sch = to_subchannel(cdev->dev.parent); + if (!sch->schib.pmcw.ena) + return -EINVAL; if (cdev->private->state == DEV_STATE_NOT_OPER) return -ENODEV; if (cdev->private->state != DEV_STATE_ONLINE && cdev->private->state != DEV_STATE_W4SENSE) return -EINVAL; - sch = to_subchannel(cdev->dev.parent); + ret = cio_clear(sch); if (ret == 0) cdev->private->intparm = intparm; @@ -162,6 +163,8 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, if (!cdev || !cdev->dev.parent) return -ENODEV; sch = to_subchannel(cdev->dev.parent); + if (!sch->schib.pmcw.ena) + return -EINVAL; if (cdev->private->state == DEV_STATE_NOT_OPER) return -ENODEV; if (cdev->private->state == DEV_STATE_VERIFY || @@ -337,12 +340,15 @@ int ccw_device_halt(struct ccw_device *cdev, unsigned long intparm) if (!cdev || !cdev->dev.parent) return -ENODEV; + sch = to_subchannel(cdev->dev.parent); + if (!sch->schib.pmcw.ena) + return -EINVAL; if (cdev->private->state == DEV_STATE_NOT_OPER) return -ENODEV; if (cdev->private->state != DEV_STATE_ONLINE && cdev->private->state != DEV_STATE_W4SENSE) return -EINVAL; - sch = to_subchannel(cdev->dev.parent); + ret = cio_halt(sch); if (ret == 0) cdev->private->intparm = intparm; @@ -369,6 +375,8 @@ int ccw_device_resume(struct ccw_device *cdev) if (!cdev || !cdev->dev.parent) return -ENODEV; sch = to_subchannel(cdev->dev.parent); + if (!sch->schib.pmcw.ena) + return -EINVAL; if (cdev->private->state == DEV_STATE_NOT_OPER) return -ENODEV; if (cdev->private->state != DEV_STATE_ONLINE || @@ -580,6 +588,8 @@ int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw, int rc; sch = to_subchannel(cdev->dev.parent); + if (!sch->schib.pmcw.ena) + return -EINVAL; if (cdev->private->state != DEV_STATE_ONLINE) return -EIO; /* Adjust requested path mask to excluded varied off paths. */ @@ -669,6 +679,8 @@ int ccw_device_tm_intrg(struct ccw_device *cdev) { struct subchannel *sch = to_subchannel(cdev->dev.parent); + if (!sch->schib.pmcw.ena) + return -EINVAL; if (cdev->private->state != DEV_STATE_ONLINE) return -EIO; if (!scsw_is_tm(&sch->schib.scsw) || diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index c4f3e7c9a85..0b8f381bd20 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -107,6 +107,7 @@ struct ccw_device_private { 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 */ } __attribute__((packed)) flags; unsigned long intparm; /* user interruption parameter */ struct qdio_irq *qdio_data; diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 7b6f46ddf3c..f370f8d460a 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -3,12 +3,12 @@ * ESCON CLAW network driver * * Linux for zSeries version - * Copyright (C) 2002,2005 IBM Corporation + * Copyright IBM Corp. 2002, 2009 * Author(s) Original code written by: - * Kazuo Iimura (iimura@jp.ibm.com) + * Kazuo Iimura <iimura@jp.ibm.com> * Rewritten by - * Andy Richter (richtera@us.ibm.com) - * Marc Price (mwprice@us.ibm.com) + * Andy Richter <richtera@us.ibm.com> + * Marc Price <mwprice@us.ibm.com> * * sysfs parms: * group x.x.rrrr,x.x.wwww @@ -253,6 +253,11 @@ static void claw_free_wrt_buf(struct net_device *dev); /* Functions for unpack reads */ static void unpack_read(struct net_device *dev); +static int claw_pm_prepare(struct ccwgroup_device *gdev) +{ + return -EPERM; +} + /* ccwgroup table */ static struct ccwgroup_driver claw_group_driver = { @@ -264,6 +269,7 @@ static struct ccwgroup_driver claw_group_driver = { .remove = claw_remove_device, .set_online = claw_new_device, .set_offline = claw_shutdown_device, + .prepare = claw_pm_prepare, }; /* @@ -284,7 +290,7 @@ claw_probe(struct ccwgroup_device *cgdev) if (!get_device(&cgdev->dev)) return -ENODEV; privptr = kzalloc(sizeof(struct claw_privbk), GFP_KERNEL); - cgdev->dev.driver_data = privptr; + dev_set_drvdata(&cgdev->dev, privptr); if (privptr == NULL) { probe_error(cgdev); put_device(&cgdev->dev); @@ -591,14 +597,14 @@ claw_irq_handler(struct ccw_device *cdev, CLAW_DBF_TEXT(4, trace, "clawirq"); /* Bypass all 'unsolicited interrupts' */ - if (!cdev->dev.driver_data) { + privptr = dev_get_drvdata(&cdev->dev); + if (!privptr) { dev_warn(&cdev->dev, "An uninitialized CLAW device received an" " IRQ, c-%02x d-%02x\n", irb->scsw.cmd.cstat, irb->scsw.cmd.dstat); CLAW_DBF_TEXT(2, trace, "badirq"); return; } - privptr = (struct claw_privbk *)cdev->dev.driver_data; /* Try to extract channel from driver data. */ if (privptr->channel[READ].cdev == cdev) @@ -1980,9 +1986,9 @@ probe_error( struct ccwgroup_device *cgdev) struct claw_privbk *privptr; CLAW_DBF_TEXT(4, trace, "proberr"); - privptr = (struct claw_privbk *) cgdev->dev.driver_data; + privptr = dev_get_drvdata(&cgdev->dev); if (privptr != NULL) { - cgdev->dev.driver_data = NULL; + dev_set_drvdata(&cgdev->dev, NULL); kfree(privptr->p_env); kfree(privptr->p_mtc_envelope); kfree(privptr); @@ -2911,9 +2917,9 @@ claw_new_device(struct ccwgroup_device *cgdev) dev_info(&cgdev->dev, "add for %s\n", dev_name(&cgdev->cdev[READ]->dev)); CLAW_DBF_TEXT(2, setup, "new_dev"); - privptr = cgdev->dev.driver_data; - cgdev->cdev[READ]->dev.driver_data = privptr; - cgdev->cdev[WRITE]->dev.driver_data = privptr; + privptr = dev_get_drvdata(&cgdev->dev); + dev_set_drvdata(&cgdev->cdev[READ]->dev, privptr); + dev_set_drvdata(&cgdev->cdev[WRITE]->dev, privptr); if (!privptr) return -ENODEV; p_env = privptr->p_env; @@ -2950,9 +2956,9 @@ claw_new_device(struct ccwgroup_device *cgdev) goto out; } dev->ml_priv = privptr; - cgdev->dev.driver_data = privptr; - cgdev->cdev[READ]->dev.driver_data = privptr; - cgdev->cdev[WRITE]->dev.driver_data = privptr; + dev_set_drvdata(&cgdev->dev, privptr); + dev_set_drvdata(&cgdev->cdev[READ]->dev, privptr); + dev_set_drvdata(&cgdev->cdev[WRITE]->dev, privptr); /* sysfs magic */ SET_NETDEV_DEV(dev, &cgdev->dev); if (register_netdev(dev) != 0) { @@ -3018,7 +3024,7 @@ claw_shutdown_device(struct ccwgroup_device *cgdev) int ret; CLAW_DBF_TEXT_(2, setup, "%s", dev_name(&cgdev->dev)); - priv = cgdev->dev.driver_data; + priv = dev_get_drvdata(&cgdev->dev); if (!priv) return -ENODEV; ndev = priv->channel[READ].ndev; @@ -3048,7 +3054,7 @@ claw_remove_device(struct ccwgroup_device *cgdev) BUG_ON(!cgdev); CLAW_DBF_TEXT_(2, setup, "%s", dev_name(&cgdev->dev)); - priv = cgdev->dev.driver_data; + priv = dev_get_drvdata(&cgdev->dev); BUG_ON(!priv); dev_info(&cgdev->dev, " will be removed.\n"); if (cgdev->state == CCWGROUP_ONLINE) @@ -3063,9 +3069,9 @@ claw_remove_device(struct ccwgroup_device *cgdev) kfree(priv->channel[1].irb); priv->channel[1].irb=NULL; kfree(priv); - cgdev->dev.driver_data=NULL; - cgdev->cdev[READ]->dev.driver_data = NULL; - cgdev->cdev[WRITE]->dev.driver_data = NULL; + dev_set_drvdata(&cgdev->dev, NULL); + dev_set_drvdata(&cgdev->cdev[READ]->dev, NULL); + dev_set_drvdata(&cgdev->cdev[WRITE]->dev, NULL); put_device(&cgdev->dev); return; @@ -3081,7 +3087,7 @@ claw_hname_show(struct device *dev, struct device_attribute *attr, char *buf) struct claw_privbk *priv; struct claw_env * p_env; - priv = dev->driver_data; + priv = dev_get_drvdata(dev); if (!priv) return -ENODEV; p_env = priv->p_env; @@ -3095,7 +3101,7 @@ claw_hname_write(struct device *dev, struct device_attribute *attr, struct claw_privbk *priv; struct claw_env * p_env; - priv = dev->driver_data; + priv = dev_get_drvdata(dev); if (!priv) return -ENODEV; p_env = priv->p_env; @@ -3119,7 +3125,7 @@ claw_adname_show(struct device *dev, struct device_attribute *attr, char *buf) struct claw_privbk *priv; struct claw_env * p_env; - priv = dev->driver_data; + priv = dev_get_drvdata(dev); if (!priv) return -ENODEV; p_env = priv->p_env; @@ -3133,7 +3139,7 @@ claw_adname_write(struct device *dev, struct device_attribute *attr, struct claw_privbk *priv; struct claw_env * p_env; - priv = dev->driver_data; + priv = dev_get_drvdata(dev); if (!priv) return -ENODEV; p_env = priv->p_env; @@ -3157,7 +3163,7 @@ claw_apname_show(struct device *dev, struct device_attribute *attr, char *buf) struct claw_privbk *priv; struct claw_env * p_env; - priv = dev->driver_data; + priv = dev_get_drvdata(dev); if (!priv) return -ENODEV; p_env = priv->p_env; @@ -3172,7 +3178,7 @@ claw_apname_write(struct device *dev, struct device_attribute *attr, struct claw_privbk *priv; struct claw_env * p_env; - priv = dev->driver_data; + priv = dev_get_drvdata(dev); if (!priv) return -ENODEV; p_env = priv->p_env; @@ -3206,7 +3212,7 @@ claw_wbuff_show(struct device *dev, struct device_attribute *attr, char *buf) struct claw_privbk *priv; struct claw_env * p_env; - priv = dev->driver_data; + priv = dev_get_drvdata(dev); if (!priv) return -ENODEV; p_env = priv->p_env; @@ -3221,7 +3227,7 @@ claw_wbuff_write(struct device *dev, struct device_attribute *attr, struct claw_env * p_env; int nnn,max; - priv = dev->driver_data; + priv = dev_get_drvdata(dev); if (!priv) return -ENODEV; p_env = priv->p_env; @@ -3248,7 +3254,7 @@ claw_rbuff_show(struct device *dev, struct device_attribute *attr, char *buf) struct claw_privbk *priv; struct claw_env * p_env; - priv = dev->driver_data; + priv = dev_get_drvdata(dev); if (!priv) return -ENODEV; p_env = priv->p_env; @@ -3263,7 +3269,7 @@ claw_rbuff_write(struct device *dev, struct device_attribute *attr, struct claw_env *p_env; int nnn,max; - priv = dev->driver_data; + priv = dev_get_drvdata(dev); if (!priv) return -ENODEV; p_env = priv->p_env; diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 54c4649a493..222e4739443 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1,7 +1,7 @@ /* * drivers/s390/net/ctcm_main.c * - * Copyright IBM Corp. 2001, 2007 + * Copyright IBM Corp. 2001, 2009 * Author(s): * Original CTC driver(s): * Fritz Elfert (felfert@millenux.com) @@ -1688,6 +1688,38 @@ static void ctcm_remove_device(struct ccwgroup_device *cgdev) put_device(&cgdev->dev); } +static int ctcm_pm_suspend(struct ccwgroup_device *gdev) +{ + struct ctcm_priv *priv = dev_get_drvdata(&gdev->dev); + + if (gdev->state == CCWGROUP_OFFLINE) + return 0; + netif_device_detach(priv->channel[READ]->netdev); + ctcm_close(priv->channel[READ]->netdev); + ccw_device_set_offline(gdev->cdev[1]); + ccw_device_set_offline(gdev->cdev[0]); + return 0; +} + +static int ctcm_pm_resume(struct ccwgroup_device *gdev) +{ + struct ctcm_priv *priv = dev_get_drvdata(&gdev->dev); + int rc; + + if (gdev->state == CCWGROUP_OFFLINE) + return 0; + rc = ccw_device_set_online(gdev->cdev[1]); + if (rc) + goto err_out; + rc = ccw_device_set_online(gdev->cdev[0]); + if (rc) + goto err_out; + ctcm_open(priv->channel[READ]->netdev); +err_out: + netif_device_attach(priv->channel[READ]->netdev); + return rc; +} + static struct ccwgroup_driver ctcm_group_driver = { .owner = THIS_MODULE, .name = CTC_DRIVER_NAME, @@ -1697,6 +1729,9 @@ static struct ccwgroup_driver ctcm_group_driver = { .remove = ctcm_remove_device, .set_online = ctcm_new_device, .set_offline = ctcm_shutdown_device, + .freeze = ctcm_pm_suspend, + .thaw = ctcm_pm_resume, + .restore = ctcm_pm_resume, }; diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index a45bc24eb5f..8c675905448 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1,15 +1,12 @@ /* - * linux/drivers/s390/net/lcs.c - * * Linux for S/390 Lan Channel Station Network Driver * - * Copyright (C) 1999-2001 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Original Code written by - * DJ Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) - * Rewritten by - * Frank Pavlic (fpavlic@de.ibm.com) and - * Martin Schwidefsky <schwidefsky@de.ibm.com> + * Copyright IBM Corp. 1999, 2009 + * Author(s): Original Code written by + * DJ Barrow <djbarrow@de.ibm.com,barrow_dj@yahoo.com> + * Rewritten by + * Frank Pavlic <fpavlic@de.ibm.com> and + * Martin Schwidefsky <schwidefsky@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 @@ -1939,7 +1936,7 @@ lcs_portno_show (struct device *dev, struct device_attribute *attr, char *buf) { struct lcs_card *card; - card = (struct lcs_card *)dev->driver_data; + card = dev_get_drvdata(dev); if (!card) return 0; @@ -1956,7 +1953,7 @@ lcs_portno_store (struct device *dev, struct device_attribute *attr, const char struct lcs_card *card; int value; - card = (struct lcs_card *)dev->driver_data; + card = dev_get_drvdata(dev); if (!card) return 0; @@ -1990,7 +1987,7 @@ lcs_timeout_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lcs_card *card; - card = (struct lcs_card *)dev->driver_data; + card = dev_get_drvdata(dev); return card ? sprintf(buf, "%u\n", card->lancmd_timeout) : 0; } @@ -2001,7 +1998,7 @@ lcs_timeout_store (struct device *dev, struct device_attribute *attr, const char struct lcs_card *card; int value; - card = (struct lcs_card *)dev->driver_data; + card = dev_get_drvdata(dev); if (!card) return 0; @@ -2020,7 +2017,7 @@ static ssize_t lcs_dev_recover_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct lcs_card *card = dev->driver_data; + struct lcs_card *card = dev_get_drvdata(dev); char *tmp; int i; @@ -2073,7 +2070,7 @@ lcs_probe_device(struct ccwgroup_device *ccwgdev) put_device(&ccwgdev->dev); return ret; } - ccwgdev->dev.driver_data = card; + dev_set_drvdata(&ccwgdev->dev, card); ccwgdev->cdev[0]->handler = lcs_irq; ccwgdev->cdev[1]->handler = lcs_irq; card->gdev = ccwgdev; @@ -2090,7 +2087,7 @@ lcs_register_netdev(struct ccwgroup_device *ccwgdev) struct lcs_card *card; LCS_DBF_TEXT(2, setup, "regnetdv"); - card = (struct lcs_card *)ccwgdev->dev.driver_data; + card = dev_get_drvdata(&ccwgdev->dev); if (card->dev->reg_state != NETREG_UNINITIALIZED) return 0; SET_NETDEV_DEV(card->dev, &ccwgdev->dev); @@ -2123,7 +2120,7 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) enum lcs_dev_states recover_state; int rc; - card = (struct lcs_card *)ccwgdev->dev.driver_data; + card = dev_get_drvdata(&ccwgdev->dev); if (!card) return -ENODEV; @@ -2229,7 +2226,7 @@ __lcs_shutdown_device(struct ccwgroup_device *ccwgdev, int recovery_mode) int ret; LCS_DBF_TEXT(3, setup, "shtdndev"); - card = (struct lcs_card *)ccwgdev->dev.driver_data; + card = dev_get_drvdata(&ccwgdev->dev); if (!card) return -ENODEV; if (recovery_mode == 0) { @@ -2296,7 +2293,7 @@ lcs_remove_device(struct ccwgroup_device *ccwgdev) { struct lcs_card *card; - card = (struct lcs_card *)ccwgdev->dev.driver_data; + card = dev_get_drvdata(&ccwgdev->dev); if (!card) return; @@ -2313,6 +2310,60 @@ lcs_remove_device(struct ccwgroup_device *ccwgdev) put_device(&ccwgdev->dev); } +static int lcs_pm_suspend(struct lcs_card *card) +{ + if (card->dev) + netif_device_detach(card->dev); + lcs_set_allowed_threads(card, 0); + lcs_wait_for_threads(card, 0xffffffff); + if (card->state != DEV_STATE_DOWN) + __lcs_shutdown_device(card->gdev, 1); + return 0; +} + +static int lcs_pm_resume(struct lcs_card *card) +{ + int rc = 0; + + if (card->state == DEV_STATE_RECOVER) + rc = lcs_new_device(card->gdev); + if (card->dev) + netif_device_attach(card->dev); + if (rc) { + dev_warn(&card->gdev->dev, "The lcs device driver " + "failed to recover the device\n"); + } + return rc; +} + +static int lcs_prepare(struct ccwgroup_device *gdev) +{ + return 0; +} + +static void lcs_complete(struct ccwgroup_device *gdev) +{ + return; +} + +static int lcs_freeze(struct ccwgroup_device *gdev) +{ + struct lcs_card *card = dev_get_drvdata(&gdev->dev); + return lcs_pm_suspend(card); +} + +static int lcs_thaw(struct ccwgroup_device *gdev) +{ + struct lcs_card *card = dev_get_drvdata(&gdev->dev); + return lcs_pm_resume(card); +} + +static int lcs_restore(struct ccwgroup_device *gdev) +{ + struct lcs_card *card = dev_get_drvdata(&gdev->dev); + return lcs_pm_resume(card); +} + /** * LCS ccwgroup driver registration */ @@ -2325,6 +2376,11 @@ static struct ccwgroup_driver lcs_group_driver = { .remove = lcs_remove_device, .set_online = lcs_new_device, .set_offline = lcs_shutdown_device, + .prepare = lcs_prepare, + .complete = lcs_complete, + .freeze = lcs_freeze, + .thaw = lcs_thaw, + .restore = lcs_restore, }; /** diff --git a/drivers/s390/net/lcs.h b/drivers/s390/net/lcs.h index d58fea52557..6d668642af2 100644 --- a/drivers/s390/net/lcs.h +++ b/drivers/s390/net/lcs.h @@ -34,8 +34,8 @@ static inline int lcs_dbf_passes(debug_info_t *dbf_grp, int level) * sysfs related stuff */ #define CARD_FROM_DEV(cdev) \ - (struct lcs_card *) \ - ((struct ccwgroup_device *)cdev->dev.driver_data)->dev.driver_data; + (struct lcs_card *) dev_get_drvdata( \ + &((struct ccwgroup_device *)dev_get_drvdata(&cdev->dev))->dev); /** * CCW commands used in this driver */ diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index aec9e5d3cf4..52574ce797b 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -1,11 +1,15 @@ /* * IUCV network driver * - * Copyright 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com) + * Copyright IBM Corp. 2001, 2009 * - * Sysfs integration and all bugs therein by Cornelia Huck - * (cornelia.huck@de.ibm.com) + * Author(s): + * Original netiucv driver: + * Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com) + * Sysfs integration and all bugs therein: + * Cornelia Huck (cornelia.huck@de.ibm.com) + * PM functions: + * Ursula Braun (ursula.braun@de.ibm.com) * * Documentation used: * the source of the original IUCV driver by: @@ -149,10 +153,27 @@ PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \ #define PRINTK_HEADER " iucv: " /* for debugging */ +/* dummy device to make sure netiucv_pm functions are called */ +static struct device *netiucv_dev; + +static int netiucv_pm_prepare(struct device *); +static void netiucv_pm_complete(struct device *); +static int netiucv_pm_freeze(struct device *); +static int netiucv_pm_restore_thaw(struct device *); + +static struct dev_pm_ops netiucv_pm_ops = { + .prepare = netiucv_pm_prepare, + .complete = netiucv_pm_complete, + .freeze = netiucv_pm_freeze, + .thaw = netiucv_pm_restore_thaw, + .restore = netiucv_pm_restore_thaw, +}; + static struct device_driver netiucv_driver = { .owner = THIS_MODULE, .name = "netiucv", .bus = &iucv_bus, + .pm = &netiucv_pm_ops, }; static int netiucv_callback_connreq(struct iucv_path *, @@ -233,6 +254,7 @@ struct netiucv_priv { fsm_instance *fsm; struct iucv_connection *conn; struct device *dev; + int pm_state; }; /** @@ -1265,6 +1287,72 @@ static int netiucv_close(struct net_device *dev) return 0; } +static int netiucv_pm_prepare(struct device *dev) +{ + IUCV_DBF_TEXT(trace, 3, __func__); + return 0; +} + +static void netiucv_pm_complete(struct device *dev) +{ + IUCV_DBF_TEXT(trace, 3, __func__); + return; +} + +/** + * netiucv_pm_freeze() - Freeze PM callback + * @dev: netiucv device + * + * close open netiucv interfaces + */ +static int netiucv_pm_freeze(struct device *dev) +{ + struct netiucv_priv *priv = dev->driver_data; + struct net_device *ndev = NULL; + int rc = 0; + + IUCV_DBF_TEXT(trace, 3, __func__); + if (priv && priv->conn) + ndev = priv->conn->netdev; + if (!ndev) + goto out; + netif_device_detach(ndev); + priv->pm_state = fsm_getstate(priv->fsm); + rc = netiucv_close(ndev); +out: + return rc; +} + +/** + * netiucv_pm_restore_thaw() - Thaw and restore PM callback + * @dev: netiucv device + * + * re-open netiucv interfaces closed during freeze + */ +static int netiucv_pm_restore_thaw(struct device *dev) +{ + struct netiucv_priv *priv = dev->driver_data; + struct net_device *ndev = NULL; + int rc = 0; + + IUCV_DBF_TEXT(trace, 3, __func__); + if (priv && priv->conn) + ndev = priv->conn->netdev; + if (!ndev) + goto out; + switch (priv->pm_state) { + case DEV_STATE_RUNNING: + case DEV_STATE_STARTWAIT: + rc = netiucv_open(ndev); + break; + default: + break; + } + netif_device_attach(ndev); +out: + return rc; +} + /** * Start transmission of a packet. * Called from generic network device layer. @@ -1364,7 +1452,7 @@ static int netiucv_change_mtu(struct net_device * dev, int new_mtu) static ssize_t user_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid)); @@ -1373,7 +1461,7 @@ static ssize_t user_show(struct device *dev, struct device_attribute *attr, static ssize_t user_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); struct net_device *ndev = priv->conn->netdev; char *p; char *tmp; @@ -1430,7 +1518,8 @@ static DEVICE_ATTR(user, 0644, user_show, user_write); static ssize_t buffer_show (struct device *dev, struct device_attribute *attr, char *buf) -{ struct netiucv_priv *priv = dev->driver_data; +{ + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%d\n", priv->conn->max_buffsize); @@ -1439,7 +1528,7 @@ static ssize_t buffer_show (struct device *dev, struct device_attribute *attr, static ssize_t buffer_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); struct net_device *ndev = priv->conn->netdev; char *e; int bs1; @@ -1487,7 +1576,7 @@ static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write); static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr, char *buf) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm)); @@ -1498,7 +1587,7 @@ static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL); static ssize_t conn_fsm_show (struct device *dev, struct device_attribute *attr, char *buf) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm)); @@ -1509,7 +1598,7 @@ static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL); static ssize_t maxmulti_show (struct device *dev, struct device_attribute *attr, char *buf) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti); @@ -1519,7 +1608,7 @@ static ssize_t maxmulti_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 4, __func__); priv->conn->prof.maxmulti = 0; @@ -1531,7 +1620,7 @@ static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write); static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr, char *buf) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue); @@ -1540,7 +1629,7 @@ static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr, static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 4, __func__); priv->conn->prof.maxcqueue = 0; @@ -1552,7 +1641,7 @@ static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write); static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr, char *buf) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%ld\n", priv->conn->prof.doios_single); @@ -1561,7 +1650,7 @@ static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr, static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 4, __func__); priv->conn->prof.doios_single = 0; @@ -1573,7 +1662,7 @@ static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write); static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr, char *buf) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi); @@ -1582,7 +1671,7 @@ static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr, static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); priv->conn->prof.doios_multi = 0; @@ -1594,7 +1683,7 @@ static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write); static ssize_t txlen_show (struct device *dev, struct device_attribute *attr, char *buf) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%ld\n", priv->conn->prof.txlen); @@ -1603,7 +1692,7 @@ static ssize_t txlen_show (struct device *dev, struct device_attribute *attr, static ssize_t txlen_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 4, __func__); priv->conn->prof.txlen = 0; @@ -1615,7 +1704,7 @@ static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write); static ssize_t txtime_show (struct device *dev, struct device_attribute *attr, char *buf) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%ld\n", priv->conn->prof.tx_time); @@ -1624,7 +1713,7 @@ static ssize_t txtime_show (struct device *dev, struct device_attribute *attr, static ssize_t txtime_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 4, __func__); priv->conn->prof.tx_time = 0; @@ -1636,7 +1725,7 @@ static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write); static ssize_t txpend_show (struct device *dev, struct device_attribute *attr, char *buf) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending); @@ -1645,7 +1734,7 @@ static ssize_t txpend_show (struct device *dev, struct device_attribute *attr, static ssize_t txpend_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 4, __func__); priv->conn->prof.tx_pending = 0; @@ -1657,7 +1746,7 @@ static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write); static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr, char *buf) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending); @@ -1666,7 +1755,7 @@ static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr, static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 4, __func__); priv->conn->prof.tx_max_pending = 0; @@ -1731,7 +1820,6 @@ static int netiucv_register_device(struct net_device *ndev) struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL); int ret; - IUCV_DBF_TEXT(trace, 3, __func__); if (dev) { @@ -1758,7 +1846,7 @@ static int netiucv_register_device(struct net_device *ndev) if (ret) goto out_unreg; priv->dev = dev; - dev->driver_data = priv; + dev_set_drvdata(dev, priv); return 0; out_unreg: @@ -2100,6 +2188,7 @@ static void __exit netiucv_exit(void) netiucv_unregister_device(dev); } + device_unregister(netiucv_dev); driver_unregister(&netiucv_driver); iucv_unregister(&netiucv_handler, 1); iucv_unregister_dbf_views(); @@ -2125,10 +2214,25 @@ static int __init netiucv_init(void) IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc); goto out_iucv; } - + /* establish dummy device */ + netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!netiucv_dev) { + rc = -ENOMEM; + goto out_driver; + } + dev_set_name(netiucv_dev, "netiucv"); + netiucv_dev->bus = &iucv_bus; + netiucv_dev->parent = iucv_root; + netiucv_dev->release = (void (*)(struct device *))kfree; + netiucv_dev->driver = &netiucv_driver; + rc = device_register(netiucv_dev); + if (rc) + goto out_driver; netiucv_banner(); return rc; +out_driver: + driver_unregister(&netiucv_driver); out_iucv: iucv_unregister(&netiucv_handler, 1); out_dbf: diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 74c49d9a8db..d53621c4acb 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1,7 +1,7 @@ /* * drivers/s390/net/qeth_core_main.c * - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007, 2009 * Author(s): Utz Bacher <utz.bacher@de.ibm.com>, * Frank Pavlic <fpavlic@de.ibm.com>, * Thomas Spatzier <tspat@de.ibm.com>, @@ -4195,6 +4195,50 @@ static void qeth_core_shutdown(struct ccwgroup_device *gdev) card->discipline.ccwgdriver->shutdown(gdev); } +static int qeth_core_prepare(struct ccwgroup_device *gdev) +{ + struct qeth_card *card = dev_get_drvdata(&gdev->dev); + if (card->discipline.ccwgdriver && + card->discipline.ccwgdriver->prepare) + return card->discipline.ccwgdriver->prepare(gdev); + return 0; +} + +static void qeth_core_complete(struct ccwgroup_device *gdev) +{ + struct qeth_card *card = dev_get_drvdata(&gdev->dev); + if (card->discipline.ccwgdriver && + card->discipline.ccwgdriver->complete) + card->discipline.ccwgdriver->complete(gdev); +} + +static int qeth_core_freeze(struct ccwgroup_device *gdev) +{ + struct qeth_card *card = dev_get_drvdata(&gdev->dev); + if (card->discipline.ccwgdriver && + card->discipline.ccwgdriver->freeze) + return card->discipline.ccwgdriver->freeze(gdev); + return 0; +} + +static int qeth_core_thaw(struct ccwgroup_device *gdev) +{ + struct qeth_card *card = dev_get_drvdata(&gdev->dev); + if (card->discipline.ccwgdriver && + card->discipline.ccwgdriver->thaw) + return card->discipline.ccwgdriver->thaw(gdev); + return 0; +} + +static int qeth_core_restore(struct ccwgroup_device *gdev) +{ + struct qeth_card *card = dev_get_drvdata(&gdev->dev); + if (card->discipline.ccwgdriver && + card->discipline.ccwgdriver->restore) + return card->discipline.ccwgdriver->restore(gdev); + return 0; +} + static struct ccwgroup_driver qeth_core_ccwgroup_driver = { .owner = THIS_MODULE, .name = "qeth", @@ -4204,6 +4248,11 @@ static struct ccwgroup_driver qeth_core_ccwgroup_driver = { .set_online = qeth_core_set_online, .set_offline = qeth_core_set_offline, .shutdown = qeth_core_shutdown, + .prepare = qeth_core_prepare, + .complete = qeth_core_complete, + .freeze = qeth_core_freeze, + .thaw = qeth_core_thaw, + .restore = qeth_core_restore, }; static ssize_t diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index ecd3d06c0d5..81d7f268418 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1,7 +1,7 @@ /* * drivers/s390/net/qeth_l2_main.c * - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007, 2009 * Author(s): Utz Bacher <utz.bacher@de.ibm.com>, * Frank Pavlic <fpavlic@de.ibm.com>, * Thomas Spatzier <tspat@de.ibm.com>, @@ -1141,12 +1141,62 @@ static void qeth_l2_shutdown(struct ccwgroup_device *gdev) qeth_clear_qdio_buffers(card); } +static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev) +{ + struct qeth_card *card = dev_get_drvdata(&gdev->dev); + + if (card->dev) + netif_device_detach(card->dev); + qeth_set_allowed_threads(card, 0, 1); + wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); + if (gdev->state == CCWGROUP_OFFLINE) + return 0; + if (card->state == CARD_STATE_UP) { + card->use_hard_stop = 1; + __qeth_l2_set_offline(card->gdev, 1); + } else + __qeth_l2_set_offline(card->gdev, 0); + return 0; +} + +static int qeth_l2_pm_resume(struct ccwgroup_device *gdev) +{ + struct qeth_card *card = dev_get_drvdata(&gdev->dev); + int rc = 0; + + if (gdev->state == CCWGROUP_OFFLINE) + goto out; + + if (card->state == CARD_STATE_RECOVER) { + rc = __qeth_l2_set_online(card->gdev, 1); + if (rc) { + if (card->dev) { + rtnl_lock(); + dev_close(card->dev); + rtnl_unlock(); + } + } + } else + rc = __qeth_l2_set_online(card->gdev, 0); +out: + qeth_set_allowed_threads(card, 0xffffffff, 0); + if (card->dev) + netif_device_attach(card->dev); + if (rc) + dev_warn(&card->gdev->dev, "The qeth device driver " + "failed to recover an error on the device\n"); + return rc; +} + struct ccwgroup_driver qeth_l2_ccwgroup_driver = { .probe = qeth_l2_probe_device, .remove = qeth_l2_remove_device, .set_online = qeth_l2_set_online, .set_offline = qeth_l2_set_offline, .shutdown = qeth_l2_shutdown, + .freeze = qeth_l2_pm_suspend, + .thaw = qeth_l2_pm_resume, + .restore = qeth_l2_pm_resume, }; EXPORT_SYMBOL_GPL(qeth_l2_ccwgroup_driver); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 6f2386e9d6e..54872406864 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1,7 +1,7 @@ /* * drivers/s390/net/qeth_l3_main.c * - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007, 2009 * Author(s): Utz Bacher <utz.bacher@de.ibm.com>, * Frank Pavlic <fpavlic@de.ibm.com>, * Thomas Spatzier <tspat@de.ibm.com>, @@ -3283,12 +3283,62 @@ static void qeth_l3_shutdown(struct ccwgroup_device *gdev) qeth_clear_qdio_buffers(card); } +static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev) +{ + struct qeth_card *card = dev_get_drvdata(&gdev->dev); + + if (card->dev) + netif_device_detach(card->dev); + qeth_set_allowed_threads(card, 0, 1); + wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); + if (gdev->state == CCWGROUP_OFFLINE) + return 0; + if (card->state == CARD_STATE_UP) { + card->use_hard_stop = 1; + __qeth_l3_set_offline(card->gdev, 1); + } else + __qeth_l3_set_offline(card->gdev, 0); + return 0; +} + +static int qeth_l3_pm_resume(struct ccwgroup_device *gdev) +{ + struct qeth_card *card = dev_get_drvdata(&gdev->dev); + int rc = 0; + + if (gdev->state == CCWGROUP_OFFLINE) + goto out; + + if (card->state == CARD_STATE_RECOVER) { + rc = __qeth_l3_set_online(card->gdev, 1); + if (rc) { + if (card->dev) { + rtnl_lock(); + dev_close(card->dev); + rtnl_unlock(); + } + } + } else + rc = __qeth_l3_set_online(card->gdev, 0); +out: + qeth_set_allowed_threads(card, 0xffffffff, 0); + if (card->dev) + netif_device_attach(card->dev); + if (rc) + dev_warn(&card->gdev->dev, "The qeth device driver " + "failed to recover an error on the device\n"); + return rc; +} + struct ccwgroup_driver qeth_l3_ccwgroup_driver = { .probe = qeth_l3_probe_device, .remove = qeth_l3_remove_device, .set_online = qeth_l3_set_online, .set_offline = qeth_l3_set_offline, .shutdown = qeth_l3_shutdown, + .freeze = qeth_l3_pm_suspend, + .thaw = qeth_l3_pm_resume, + .restore = qeth_l3_pm_resume, }; EXPORT_SYMBOL_GPL(qeth_l3_ccwgroup_driver); diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index 164e090c262..e76a320d373 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c @@ -1,7 +1,8 @@ /* * IUCV special message driver * - * Copyright 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright IBM Corp. 2003, 2009 + * * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) * * This program is free software; you can redistribute it and/or modify @@ -40,6 +41,8 @@ MODULE_AUTHOR MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver"); static struct iucv_path *smsg_path; +/* dummy device used as trigger for PM functions */ +static struct device *smsg_dev; static DEFINE_SPINLOCK(smsg_list_lock); static LIST_HEAD(smsg_list); @@ -132,14 +135,51 @@ void smsg_unregister_callback(char *prefix, kfree(cb); } +static int smsg_pm_freeze(struct device *dev) +{ +#ifdef CONFIG_PM_DEBUG + printk(KERN_WARNING "smsg_pm_freeze\n"); +#endif + if (smsg_path) + iucv_path_sever(smsg_path, NULL); + return 0; +} + +static int smsg_pm_restore_thaw(struct device *dev) +{ + int rc; + +#ifdef CONFIG_PM_DEBUG + printk(KERN_WARNING "smsg_pm_restore_thaw\n"); +#endif + if (smsg_path) { + memset(smsg_path, 0, sizeof(*smsg_path)); + smsg_path->msglim = 255; + smsg_path->flags = 0; + rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ", + NULL, NULL, NULL); + printk(KERN_ERR "iucv_path_connect returned with rc %i\n", rc); + } + return 0; +} + +static struct dev_pm_ops smsg_pm_ops = { + .freeze = smsg_pm_freeze, + .thaw = smsg_pm_restore_thaw, + .restore = smsg_pm_restore_thaw, +}; + static struct device_driver smsg_driver = { + .owner = THIS_MODULE, .name = "SMSGIUCV", .bus = &iucv_bus, + .pm = &smsg_pm_ops, }; static void __exit smsg_exit(void) { cpcmd("SET SMSG IUCV", NULL, 0, NULL); + device_unregister(smsg_dev); iucv_unregister(&smsg_handler, 1); driver_unregister(&smsg_driver); } @@ -166,12 +206,29 @@ static int __init smsg_init(void) rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ", NULL, NULL, NULL); if (rc) - goto out_free; + goto out_free_path; + smsg_dev = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!smsg_dev) { + rc = -ENOMEM; + goto out_free_path; + } + dev_set_name(smsg_dev, "smsg_iucv"); + smsg_dev->bus = &iucv_bus; + smsg_dev->parent = iucv_root; + smsg_dev->release = (void (*)(struct device *))kfree; + smsg_dev->driver = &smsg_driver; + rc = device_register(smsg_dev); + if (rc) + goto out_free_dev; + cpcmd("SET SMSG IUCV", NULL, 0, NULL); return 0; -out_free: +out_free_dev: + kfree(smsg_dev); +out_free_path: iucv_path_free(smsg_path); + smsg_path = NULL; out_register: iucv_unregister(&smsg_handler, 1); out_driver: diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index b2fe5cdbcae..d9da5c42ccb 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -13,6 +13,36 @@ #define ZFCP_MODEL_PRIV 0x4 +static int zfcp_ccw_suspend(struct ccw_device *cdev) + +{ + struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev); + + down(&zfcp_data.config_sema); + + zfcp_erp_adapter_shutdown(adapter, 0, "ccsusp1", NULL); + zfcp_erp_wait(adapter); + + up(&zfcp_data.config_sema); + + return 0; +} + +static int zfcp_ccw_activate(struct ccw_device *cdev) + +{ + struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev); + + zfcp_erp_modify_adapter_status(adapter, "ccresu1", NULL, + ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET); + zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, + "ccresu2", NULL); + zfcp_erp_wait(adapter); + flush_work(&adapter->scan_work); + + return 0; +} + static struct ccw_device_id zfcp_ccw_device_id[] = { { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) }, { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, ZFCP_MODEL_PRIV) }, @@ -227,6 +257,9 @@ static struct ccw_driver zfcp_ccw_driver = { .set_offline = zfcp_ccw_set_offline, .notify = zfcp_ccw_notify, .shutdown = zfcp_ccw_shutdown, + .freeze = zfcp_ccw_suspend, + .thaw = zfcp_ccw_activate, + .restore = zfcp_ccw_activate, }; /** diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c index 124f660a038..75ac19b1192 100644 --- a/drivers/sbus/char/openprom.c +++ b/drivers/sbus/char/openprom.c @@ -303,7 +303,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, struct device_node *dp) { DATA *data = file->private_data; - struct openpromio *opp; + struct openpromio *opp = NULL; int bufsize, error = 0; static int cnt; void __user *argp = (void __user *)arg; diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c index ed0e3e55652..538135783aa 100644 --- a/drivers/scsi/aha1740.c +++ b/drivers/scsi/aha1740.c @@ -646,7 +646,7 @@ static int aha1740_probe (struct device *dev) static __devexit int aha1740_remove (struct device *dev) { - struct Scsi_Host *shpnt = dev->driver_data; + struct Scsi_Host *shpnt = dev_get_drvdata(dev); struct aha1740_hostdata *host = HOSTDATA (shpnt); scsi_remove_host(shpnt); diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 11d2602ae88..869a11bdccb 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -1877,7 +1877,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) unsigned long wait_switch = 0; int rc; - vdev->dev.driver_data = NULL; + dev_set_drvdata(&vdev->dev, NULL); host = scsi_host_alloc(&driver_template, sizeof(*hostdata)); if (!host) { @@ -1949,7 +1949,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) scsi_scan_host(host); } - vdev->dev.driver_data = hostdata; + dev_set_drvdata(&vdev->dev, hostdata); return 0; add_srp_port_failed: @@ -1968,7 +1968,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) static int ibmvscsi_remove(struct vio_dev *vdev) { - struct ibmvscsi_host_data *hostdata = vdev->dev.driver_data; + struct ibmvscsi_host_data *hostdata = dev_get_drvdata(&vdev->dev); unmap_persist_bufs(hostdata); release_event_pool(&hostdata->pool, hostdata); ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c index e2dd6a45924..d5eaf972710 100644 --- a/drivers/scsi/ibmvscsi/ibmvstgt.c +++ b/drivers/scsi/ibmvscsi/ibmvstgt.c @@ -892,7 +892,7 @@ free_vport: static int ibmvstgt_remove(struct vio_dev *dev) { - struct srp_target *target = (struct srp_target *) dev->dev.driver_data; + struct srp_target *target = dev_get_drvdata(&dev->dev); struct Scsi_Host *shost = target->shost; struct vio_port *vport = target->ldata; diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c index 15e2d132e8b..2742ae8a3d0 100644 --- a/drivers/scsi/libsrp.c +++ b/drivers/scsi/libsrp.c @@ -135,7 +135,7 @@ int srp_target_alloc(struct srp_target *target, struct device *dev, INIT_LIST_HEAD(&target->cmd_queue); target->dev = dev; - target->dev->driver_data = target; + dev_set_drvdata(target->dev, target); target->srp_iu_size = iu_size; target->rx_ring_size = nr; diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 2b02b1fb39a..8d0f0de76b6 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -53,8 +53,7 @@ * debugfs interface * * To access this interface the user should: - * # mkdir /debug - * # mount -t debugfs none /debug + * # mount -t debugfs none /sys/kernel/debug * * The lpfc debugfs directory hierarchy is: * lpfc/lpfcX/vportY diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index b3497d7e535..338b15c0a54 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -1104,11 +1104,13 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, /* update the per-port timeout */ uart_update_timeout(port, termios->c_cflag, baud); - /* save/disable interrupts and drain transmitter */ + /* + * save/disable interrupts. The tty layer will ensure that the + * transmitter is empty if requested by the caller, so there's + * no need to wait for it here. + */ imr = UART_GET_IMR(port); UART_PUT_IDR(port, -1); - while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY)) - cpu_relax(); /* disable receiver and transmitter */ UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS); diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index 285b414f305..5d7b58f1fe4 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -924,11 +924,13 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, rational_best_approximation(16 * div * baud, sport->port.uartclk, 1 << 16, 1 << 16, &num, &denom); - tdiv64 = sport->port.uartclk; - tdiv64 *= num; - do_div(tdiv64, denom * 16 * div); - tty_encode_baud_rate(sport->port.info->port.tty, - (speed_t)tdiv64, (speed_t)tdiv64); + if (port->info && port->info->port.tty) { + tdiv64 = sport->port.uartclk; + tdiv64 *= num; + do_div(tdiv64, denom * 16 * div); + tty_encode_baud_rate(sport->port.info->port.tty, + (speed_t)tdiv64, (speed_t)tdiv64); + } num -= 1; denom -= 1; diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c index 14f8fa9135b..54483cd3529 100644 --- a/drivers/serial/of_serial.c +++ b/drivers/serial/of_serial.c @@ -122,7 +122,7 @@ static int __devinit of_platform_serial_probe(struct of_device *ofdev, info->type = port_type; info->line = ret; - ofdev->dev.driver_data = info; + dev_set_drvdata(&ofdev->dev, info); return 0; out: kfree(info); @@ -135,7 +135,7 @@ out: */ static int of_platform_serial_remove(struct of_device *ofdev) { - struct of_serial_info *info = ofdev->dev.driver_data; + struct of_serial_info *info = dev_get_drvdata(&ofdev->dev); switch (info->type) { #ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index f014cc21e81..011c5bddba6 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -803,7 +803,7 @@ static void bfin_spi_pump_transfers(unsigned long data) drv_data->rx, drv_data->len_in_bytes); /* invalidate caches, if needed */ - if (bfin_addr_dcachable((unsigned long) drv_data->rx)) + if (bfin_addr_dcacheable((unsigned long) drv_data->rx)) invalidate_dcache_range((unsigned long) drv_data->rx, (unsigned long) (drv_data->rx + drv_data->len_in_bytes)); @@ -816,7 +816,7 @@ static void bfin_spi_pump_transfers(unsigned long data) dev_dbg(&drv_data->pdev->dev, "doing DMA out.\n"); /* flush caches, if needed */ - if (bfin_addr_dcachable((unsigned long) drv_data->tx)) + if (bfin_addr_dcacheable((unsigned long) drv_data->tx)) flush_dcache_range((unsigned long) drv_data->tx, (unsigned long) (drv_data->tx + drv_data->len_in_bytes)); diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 5e38ba10a3a..0a69672097a 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -417,7 +417,7 @@ static LIST_HEAD(thermal_hwmon_list); static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_hwmon_device *hwmon = dev->driver_data; + struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev); return sprintf(buf, "%s\n", hwmon->type); } static DEVICE_ATTR(name, 0444, name_show, NULL); @@ -488,7 +488,7 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) result = PTR_ERR(hwmon->device); goto free_mem; } - hwmon->device->driver_data = hwmon; + dev_set_drvdata(hwmon->device, hwmon); result = device_create_file(hwmon->device, &dev_attr_name); if (result) goto unregister_hwmon_device; diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 9cf9ff69e3e..d171b563e94 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -306,6 +306,7 @@ enum { #define FW_GET_BYTE(p) *((__u8 *) (p)) #define FW_DIR "ueagle-atm/" +#define UEA_FW_NAME_MAX 30 #define NB_MODEM 4 #define BULK_TIMEOUT 300 @@ -1564,9 +1565,9 @@ static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver) file = cmv_file[sc->modem_index]; strcpy(cmv_name, FW_DIR); - strlcat(cmv_name, file, FIRMWARE_NAME_MAX); + strlcat(cmv_name, file, UEA_FW_NAME_MAX); if (ver == 2) - strlcat(cmv_name, ".v2", FIRMWARE_NAME_MAX); + strlcat(cmv_name, ".v2", UEA_FW_NAME_MAX); } static int request_cmvs_old(struct uea_softc *sc, @@ -1574,7 +1575,7 @@ static int request_cmvs_old(struct uea_softc *sc, { int ret, size; u8 *data; - char cmv_name[FIRMWARE_NAME_MAX]; /* 30 bytes stack variable */ + char cmv_name[UEA_FW_NAME_MAX]; /* 30 bytes stack variable */ cmvs_file_name(sc, cmv_name, 1); ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev); @@ -1608,7 +1609,7 @@ static int request_cmvs(struct uea_softc *sc, int ret, size; u32 crc; u8 *data; - char cmv_name[FIRMWARE_NAME_MAX]; /* 30 bytes stack variable */ + char cmv_name[UEA_FW_NAME_MAX]; /* 30 bytes stack variable */ cmvs_file_name(sc, cmv_name, 2); ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev); diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index d2747a49b97..26c09f0257d 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -1057,8 +1057,14 @@ static const struct file_operations usblp_fops = { .release = usblp_release, }; +static char *usblp_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + static struct usb_class_driver usblp_class = { .name = "lp%d", + .nodename = usblp_nodename, .fops = &usblp_fops, .minor_base = USBLP_MINOR_BASE, }; diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 997e659ff69..5cef88929b3 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -67,6 +67,16 @@ static struct usb_class { struct class *class; } *usb_class; +static char *usb_nodename(struct device *dev) +{ + struct usb_class_driver *drv; + + drv = dev_get_drvdata(dev); + if (!drv || !drv->nodename) + return NULL; + return drv->nodename(dev); +} + static int init_usb_class(void) { int result = 0; @@ -90,6 +100,7 @@ static int init_usb_class(void) kfree(usb_class); usb_class = NULL; } + usb_class->class->nodename = usb_nodename; exit: return result; @@ -198,7 +209,7 @@ int usb_register_dev(struct usb_interface *intf, else temp = name; intf->usb_dev = device_create(usb_class->class, &intf->dev, - MKDEV(USB_MAJOR, minor), NULL, + MKDEV(USB_MAJOR, minor), class_driver, "%s", temp); if (IS_ERR(intf->usb_dev)) { down_write(&minor_rwsem); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 55b8d3a22d2..a26f73880c3 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -310,10 +310,21 @@ static struct dev_pm_ops usb_device_pm_ops = { #endif /* CONFIG_PM */ + +static char *usb_nodename(struct device *dev) +{ + struct usb_device *usb_dev; + + usb_dev = to_usb_device(dev); + return kasprintf(GFP_KERNEL, "bus/usb/%03d/%03d", + usb_dev->bus->busnum, usb_dev->devnum); +} + struct device_type usb_device_type = { .name = "usb_device", .release = usb_release_dev, .uevent = usb_dev_uevent, + .nodename = usb_nodename, .pm = &usb_device_pm_ops, }; diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index e954225cd76..72bae8f39d8 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1574,7 +1574,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) udc->driver = driver; udc->gadget.dev.driver = &driver->driver; - udc->gadget.dev.driver_data = &driver->driver; + dev_set_drvdata(&udc->gadget.dev, &driver->driver); udc->enabled = 1; udc->selfpowered = 1; @@ -1583,7 +1583,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) DBG("driver->bind() returned %d\n", retval); udc->driver = NULL; udc->gadget.dev.driver = NULL; - udc->gadget.dev.driver_data = NULL; + dev_set_drvdata(&udc->gadget.dev, NULL); udc->enabled = 0; udc->selfpowered = 0; return retval; @@ -1613,7 +1613,7 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) driver->unbind(&udc->gadget); udc->gadget.dev.driver = NULL; - udc->gadget.dev.driver_data = NULL; + dev_set_drvdata(&udc->gadget.dev, NULL); udc->driver = NULL; DBG("unbound from %s\n", driver->driver.name); diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 4b4df23efdd..eecd2a0680a 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -163,7 +163,7 @@ static int ps3_ehci_probe(struct ps3_system_bus_device *dev) dev_dbg(&dev->core, "%s:%d: virq %lu\n", __func__, __LINE__, (unsigned long)virq); - ps3_system_bus_set_driver_data(dev, hcd); + ps3_system_bus_set_drvdata(dev, hcd); result = usb_add_hcd(hcd, virq, IRQF_DISABLED); @@ -196,8 +196,7 @@ fail_start: static int ps3_ehci_remove(struct ps3_system_bus_device *dev) { unsigned int tmp; - struct usb_hcd *hcd = - (struct usb_hcd *)ps3_system_bus_get_driver_data(dev); + struct usb_hcd *hcd = ps3_system_bus_get_drvdata(dev); BUG_ON(!hcd); @@ -209,7 +208,7 @@ static int ps3_ehci_remove(struct ps3_system_bus_device *dev) ehci_shutdown(hcd); usb_remove_hcd(hcd); - ps3_system_bus_set_driver_data(dev, NULL); + ps3_system_bus_set_drvdata(dev, NULL); BUG_ON(!hcd->regs); iounmap(hcd->regs); diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index 3d191031732..1d56259c5db 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -162,7 +162,7 @@ static int ps3_ohci_probe(struct ps3_system_bus_device *dev) dev_dbg(&dev->core, "%s:%d: virq %lu\n", __func__, __LINE__, (unsigned long)virq); - ps3_system_bus_set_driver_data(dev, hcd); + ps3_system_bus_set_drvdata(dev, hcd); result = usb_add_hcd(hcd, virq, IRQF_DISABLED); @@ -195,8 +195,7 @@ fail_start: static int ps3_ohci_remove(struct ps3_system_bus_device *dev) { unsigned int tmp; - struct usb_hcd *hcd = - (struct usb_hcd *)ps3_system_bus_get_driver_data(dev); + struct usb_hcd *hcd = ps3_system_bus_get_drvdata(dev); BUG_ON(!hcd); @@ -208,7 +207,7 @@ static int ps3_ohci_remove(struct ps3_system_bus_device *dev) ohci_shutdown(hcd); usb_remove_hcd(hcd); - ps3_system_bus_set_driver_data(dev, NULL); + ps3_system_bus_set_drvdata(dev, NULL); BUG_ON(!hcd->regs); iounmap(hcd->regs); diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index a4ef77ef917..3c5fe5cee05 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -726,12 +726,18 @@ static const struct file_operations iowarrior_fops = { .poll = iowarrior_poll, }; +static char *iowarrior_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + /* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with devfs and the driver core */ static struct usb_class_driver iowarrior_class = { .name = "iowarrior%d", + .nodename = iowarrior_nodename, .fops = &iowarrior_fops, .minor_base = IOWARRIOR_MINOR_BASE, }; diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index ab0f3226158..c1e2433f640 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -266,12 +266,18 @@ static const struct file_operations tower_fops = { .llseek = tower_llseek, }; +static char *legousbtower_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + /* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with the driver core */ static struct usb_class_driver tower_class = { .name = "legousbtower%d", + .nodename = legousbtower_nodename, .fops = &tower_fops, .minor_base = LEGO_USB_TOWER_MINOR_BASE, }; diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c index 1e35ba6f18e..b0b147cb4cb 100644 --- a/drivers/video/bw2.c +++ b/drivers/video/bw2.c @@ -111,9 +111,7 @@ struct bw2_par { u32 flags; #define BW2_FLAG_BLANKED 0x00000001 - unsigned long physbase; unsigned long which_io; - unsigned long fbsize; }; /** @@ -167,17 +165,15 @@ static int bw2_mmap(struct fb_info *info, struct vm_area_struct *vma) struct bw2_par *par = (struct bw2_par *)info->par; return sbusfb_mmap_helper(bw2_mmap_map, - par->physbase, par->fbsize, + info->fix.smem_start, info->fix.smem_len, par->which_io, vma); } static int bw2_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { - struct bw2_par *par = (struct bw2_par *) info->par; - return sbusfb_ioctl_helper(cmd, arg, info, - FBTYPE_SUN2BW, 1, par->fbsize); + FBTYPE_SUN2BW, 1, info->fix.smem_len); } /* @@ -294,7 +290,7 @@ static int __devinit bw2_probe(struct of_device *op, const struct of_device_id * spin_lock_init(&par->lock); - par->physbase = op->resource[0].start; + info->fix.smem_start = op->resource[0].start; par->which_io = op->resource[0].flags & IORESOURCE_BITS; sbusfb_fill_var(&info->var, dp, 1); @@ -317,13 +313,13 @@ static int __devinit bw2_probe(struct of_device *op, const struct of_device_id * goto out_unmap_regs; } - par->fbsize = PAGE_ALIGN(linebytes * info->var.yres); + info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); info->flags = FBINFO_DEFAULT; info->fbops = &bw2_ops; info->screen_base = of_ioremap(&op->resource[0], 0, - par->fbsize, "bw2 ram"); + info->fix.smem_len, "bw2 ram"); if (!info->screen_base) goto out_unmap_regs; @@ -338,12 +334,12 @@ static int __devinit bw2_probe(struct of_device *op, const struct of_device_id * dev_set_drvdata(&op->dev, info); printk(KERN_INFO "%s: bwtwo at %lx:%lx\n", - dp->full_name, par->which_io, par->physbase); + dp->full_name, par->which_io, info->fix.smem_start); return 0; out_unmap_screen: - of_iounmap(&op->resource[0], info->screen_base, par->fbsize); + of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len); out_unmap_regs: of_iounmap(&op->resource[0], par->regs, sizeof(struct bw2_regs)); @@ -363,7 +359,7 @@ static int __devexit bw2_remove(struct of_device *op) unregister_framebuffer(info); of_iounmap(&op->resource[0], par->regs, sizeof(struct bw2_regs)); - of_iounmap(&op->resource[0], info->screen_base, par->fbsize); + of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len); framebuffer_release(info); diff --git a/drivers/video/cg14.c b/drivers/video/cg14.c index a2d1882791a..fe45a3b8d0e 100644 --- a/drivers/video/cg14.c +++ b/drivers/video/cg14.c @@ -196,9 +196,7 @@ struct cg14_par { u32 flags; #define CG14_FLAG_BLANKED 0x00000001 - unsigned long physbase; unsigned long iospace; - unsigned long fbsize; struct sbus_mmap_map mmap_map[CG14_MMAP_ENTRIES]; @@ -271,7 +269,7 @@ static int cg14_mmap(struct fb_info *info, struct vm_area_struct *vma) struct cg14_par *par = (struct cg14_par *) info->par; return sbusfb_mmap_helper(par->mmap_map, - par->physbase, par->fbsize, + info->fix.smem_start, info->fix.smem_len, par->iospace, vma); } @@ -343,7 +341,8 @@ static int cg14_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) default: ret = sbusfb_ioctl_helper(cmd, arg, info, - FBTYPE_MDICOLOR, 8, par->fbsize); + FBTYPE_MDICOLOR, 8, + info->fix.smem_len); break; }; @@ -462,7 +461,7 @@ static void cg14_unmap_regs(struct of_device *op, struct fb_info *info, par->cursor, sizeof(struct cg14_cursor)); if (info->screen_base) of_iounmap(&op->resource[1], - info->screen_base, par->fbsize); + info->screen_base, info->fix.smem_len); } static int __devinit cg14_probe(struct of_device *op, const struct of_device_id *match) @@ -488,14 +487,14 @@ static int __devinit cg14_probe(struct of_device *op, const struct of_device_id linebytes = of_getintprop_default(dp, "linebytes", info->var.xres); - par->fbsize = PAGE_ALIGN(linebytes * info->var.yres); + info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); if (!strcmp(dp->parent->name, "sbus") || !strcmp(dp->parent->name, "sbi")) { - par->physbase = op->resource[0].start; + info->fix.smem_start = op->resource[0].start; par->iospace = op->resource[0].flags & IORESOURCE_BITS; } else { - par->physbase = op->resource[1].start; + info->fix.smem_start = op->resource[1].start; par->iospace = op->resource[0].flags & IORESOURCE_BITS; } @@ -507,7 +506,7 @@ static int __devinit cg14_probe(struct of_device *op, const struct of_device_id sizeof(struct cg14_cursor), "cg14 cursor"); info->screen_base = of_ioremap(&op->resource[1], 0, - par->fbsize, "cg14 ram"); + info->fix.smem_len, "cg14 ram"); if (!par->regs || !par->clut || !par->cursor || !info->screen_base) goto out_unmap_regs; @@ -557,7 +556,7 @@ static int __devinit cg14_probe(struct of_device *op, const struct of_device_id printk(KERN_INFO "%s: cgfourteen at %lx:%lx, %dMB\n", dp->full_name, - par->iospace, par->physbase, + par->iospace, info->fix.smem_start, par->ramsize >> 20); return 0; diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c index 99f87fb61d0..b2319fa7286 100644 --- a/drivers/video/cg3.c +++ b/drivers/video/cg3.c @@ -118,9 +118,7 @@ struct cg3_par { #define CG3_FLAG_BLANKED 0x00000001 #define CG3_FLAG_RDI 0x00000002 - unsigned long physbase; unsigned long which_io; - unsigned long fbsize; }; /** @@ -231,17 +229,15 @@ static int cg3_mmap(struct fb_info *info, struct vm_area_struct *vma) struct cg3_par *par = (struct cg3_par *)info->par; return sbusfb_mmap_helper(cg3_mmap_map, - par->physbase, par->fbsize, + info->fix.smem_start, info->fix.smem_len, par->which_io, vma); } static int cg3_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { - struct cg3_par *par = (struct cg3_par *) info->par; - return sbusfb_ioctl_helper(cmd, arg, info, - FBTYPE_SUN3COLOR, 8, par->fbsize); + FBTYPE_SUN3COLOR, 8, info->fix.smem_len); } /* @@ -368,7 +364,7 @@ static int __devinit cg3_probe(struct of_device *op, spin_lock_init(&par->lock); - par->physbase = op->resource[0].start; + info->fix.smem_start = op->resource[0].start; par->which_io = op->resource[0].flags & IORESOURCE_BITS; sbusfb_fill_var(&info->var, dp, 8); @@ -382,7 +378,7 @@ static int __devinit cg3_probe(struct of_device *op, linebytes = of_getintprop_default(dp, "linebytes", info->var.xres); - par->fbsize = PAGE_ALIGN(linebytes * info->var.yres); + info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); par->regs = of_ioremap(&op->resource[0], CG3_REGS_OFFSET, sizeof(struct cg3_regs), "cg3 regs"); @@ -392,7 +388,7 @@ static int __devinit cg3_probe(struct of_device *op, info->flags = FBINFO_DEFAULT; info->fbops = &cg3_ops; info->screen_base = of_ioremap(&op->resource[0], CG3_RAM_OFFSET, - par->fbsize, "cg3 ram"); + info->fix.smem_len, "cg3 ram"); if (!info->screen_base) goto out_unmap_regs; @@ -418,7 +414,7 @@ static int __devinit cg3_probe(struct of_device *op, dev_set_drvdata(&op->dev, info); printk(KERN_INFO "%s: cg3 at %lx:%lx\n", - dp->full_name, par->which_io, par->physbase); + dp->full_name, par->which_io, info->fix.smem_start); return 0; @@ -426,7 +422,7 @@ out_dealloc_cmap: fb_dealloc_cmap(&info->cmap); out_unmap_screen: - of_iounmap(&op->resource[0], info->screen_base, par->fbsize); + of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len); out_unmap_regs: of_iounmap(&op->resource[0], par->regs, sizeof(struct cg3_regs)); @@ -447,7 +443,7 @@ static int __devexit cg3_remove(struct of_device *op) fb_dealloc_cmap(&info->cmap); of_iounmap(&op->resource[0], par->regs, sizeof(struct cg3_regs)); - of_iounmap(&op->resource[0], info->screen_base, par->fbsize); + of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len); framebuffer_release(info); diff --git a/drivers/video/cg6.c b/drivers/video/cg6.c index 940ec04f0f1..0d47c6030e3 100644 --- a/drivers/video/cg6.c +++ b/drivers/video/cg6.c @@ -263,9 +263,7 @@ struct cg6_par { u32 flags; #define CG6_FLAG_BLANKED 0x00000001 - unsigned long physbase; unsigned long which_io; - unsigned long fbsize; }; static int cg6_sync(struct fb_info *info) @@ -596,16 +594,14 @@ static int cg6_mmap(struct fb_info *info, struct vm_area_struct *vma) struct cg6_par *par = (struct cg6_par *)info->par; return sbusfb_mmap_helper(cg6_mmap_map, - par->physbase, par->fbsize, + info->fix.smem_start, info->fix.smem_len, par->which_io, vma); } static int cg6_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { - struct cg6_par *par = (struct cg6_par *)info->par; - return sbusfb_ioctl_helper(cmd, arg, info, - FBTYPE_SUNFAST_COLOR, 8, par->fbsize); + FBTYPE_SUNFAST_COLOR, 8, info->fix.smem_len); } /* @@ -631,12 +627,12 @@ static void __devinit cg6_init_fix(struct fb_info *info, int linebytes) break; }; if (((conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK) >= 11) { - if (par->fbsize <= 0x100000) + if (info->fix.smem_len <= 0x100000) cg6_card_name = "TGX"; else cg6_card_name = "TGX+"; } else { - if (par->fbsize <= 0x100000) + if (info->fix.smem_len <= 0x100000) cg6_card_name = "GX"; else cg6_card_name = "GX+"; @@ -738,7 +734,8 @@ static void cg6_unmap_regs(struct of_device *op, struct fb_info *info, of_iounmap(&op->resource[0], par->fhc, sizeof(u32)); if (info->screen_base) - of_iounmap(&op->resource[0], info->screen_base, par->fbsize); + of_iounmap(&op->resource[0], info->screen_base, + info->fix.smem_len); } static int __devinit cg6_probe(struct of_device *op, @@ -759,7 +756,7 @@ static int __devinit cg6_probe(struct of_device *op, spin_lock_init(&par->lock); - par->physbase = op->resource[0].start; + info->fix.smem_start = op->resource[0].start; par->which_io = op->resource[0].flags & IORESOURCE_BITS; sbusfb_fill_var(&info->var, dp, 8); @@ -769,11 +766,11 @@ static int __devinit cg6_probe(struct of_device *op, linebytes = of_getintprop_default(dp, "linebytes", info->var.xres); - par->fbsize = PAGE_ALIGN(linebytes * info->var.yres); + info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); dblbuf = of_getintprop_default(dp, "dblbuf", 0); if (dblbuf) - par->fbsize *= 4; + info->fix.smem_len *= 4; par->fbc = of_ioremap(&op->resource[0], CG6_FBC_OFFSET, 4096, "cgsix fbc"); @@ -792,7 +789,7 @@ static int __devinit cg6_probe(struct of_device *op, info->fbops = &cg6_ops; info->screen_base = of_ioremap(&op->resource[0], CG6_RAM_OFFSET, - par->fbsize, "cgsix ram"); + info->fix.smem_len, "cgsix ram"); if (!par->fbc || !par->tec || !par->thc || !par->bt || !par->fhc || !info->screen_base) goto out_unmap_regs; @@ -817,7 +814,7 @@ static int __devinit cg6_probe(struct of_device *op, printk(KERN_INFO "%s: CGsix [%s] at %lx:%lx\n", dp->full_name, info->fix.id, - par->which_io, par->physbase); + par->which_io, info->fix.smem_start); return 0; diff --git a/drivers/video/leo.c b/drivers/video/leo.c index 7c7e8c2da9d..e145e2d16fe 100644 --- a/drivers/video/leo.c +++ b/drivers/video/leo.c @@ -191,9 +191,7 @@ struct leo_par { u32 flags; #define LEO_FLAG_BLANKED 0x00000001 - unsigned long physbase; unsigned long which_io; - unsigned long fbsize; }; static void leo_wait(struct leo_lx_krn __iomem *lx_krn) @@ -420,16 +418,14 @@ static int leo_mmap(struct fb_info *info, struct vm_area_struct *vma) struct leo_par *par = (struct leo_par *)info->par; return sbusfb_mmap_helper(leo_mmap_map, - par->physbase, par->fbsize, + info->fix.smem_start, info->fix.smem_len, par->which_io, vma); } static int leo_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { - struct leo_par *par = (struct leo_par *) info->par; - return sbusfb_ioctl_helper(cmd, arg, info, - FBTYPE_SUNLEO, 32, par->fbsize); + FBTYPE_SUNLEO, 32, info->fix.smem_len); } /* @@ -569,7 +565,7 @@ static int __devinit leo_probe(struct of_device *op, spin_lock_init(&par->lock); - par->physbase = op->resource[0].start; + info->fix.smem_start = op->resource[0].start; par->which_io = op->resource[0].flags & IORESOURCE_BITS; sbusfb_fill_var(&info->var, dp, 32); @@ -577,7 +573,7 @@ static int __devinit leo_probe(struct of_device *op, linebytes = of_getintprop_default(dp, "linebytes", info->var.xres); - par->fbsize = PAGE_ALIGN(linebytes * info->var.yres); + info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); par->lc_ss0_usr = of_ioremap(&op->resource[0], LEO_OFF_LC_SS0_USR, @@ -627,7 +623,7 @@ static int __devinit leo_probe(struct of_device *op, printk(KERN_INFO "%s: leo at %lx:%lx\n", dp->full_name, - par->which_io, par->physbase); + par->which_io, info->fix.smem_start); return 0; diff --git a/drivers/video/p9100.c b/drivers/video/p9100.c index 7000f2cd585..7fa4ab01b0d 100644 --- a/drivers/video/p9100.c +++ b/drivers/video/p9100.c @@ -134,9 +134,7 @@ struct p9100_par { u32 flags; #define P9100_FLAG_BLANKED 0x00000001 - unsigned long physbase; unsigned long which_io; - unsigned long fbsize; }; /** @@ -224,18 +222,16 @@ static int p9100_mmap(struct fb_info *info, struct vm_area_struct *vma) struct p9100_par *par = (struct p9100_par *)info->par; return sbusfb_mmap_helper(p9100_mmap_map, - par->physbase, par->fbsize, + info->fix.smem_start, info->fix.smem_len, par->which_io, vma); } static int p9100_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { - struct p9100_par *par = (struct p9100_par *) info->par; - /* Make it look like a cg3. */ return sbusfb_ioctl_helper(cmd, arg, info, - FBTYPE_SUN3COLOR, 8, par->fbsize); + FBTYPE_SUN3COLOR, 8, info->fix.smem_len); } /* @@ -271,7 +267,7 @@ static int __devinit p9100_probe(struct of_device *op, const struct of_device_id spin_lock_init(&par->lock); /* This is the framebuffer and the only resource apps can mmap. */ - par->physbase = op->resource[2].start; + info->fix.smem_start = op->resource[2].start; par->which_io = op->resource[2].flags & IORESOURCE_BITS; sbusfb_fill_var(&info->var, dp, 8); @@ -280,7 +276,7 @@ static int __devinit p9100_probe(struct of_device *op, const struct of_device_id info->var.blue.length = 8; linebytes = of_getintprop_default(dp, "linebytes", info->var.xres); - par->fbsize = PAGE_ALIGN(linebytes * info->var.yres); + info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); par->regs = of_ioremap(&op->resource[0], 0, sizeof(struct p9100_regs), "p9100 regs"); @@ -290,7 +286,7 @@ static int __devinit p9100_probe(struct of_device *op, const struct of_device_id info->flags = FBINFO_DEFAULT; info->fbops = &p9100_ops; info->screen_base = of_ioremap(&op->resource[2], 0, - par->fbsize, "p9100 ram"); + info->fix.smem_len, "p9100 ram"); if (!info->screen_base) goto out_unmap_regs; @@ -311,7 +307,7 @@ static int __devinit p9100_probe(struct of_device *op, const struct of_device_id printk(KERN_INFO "%s: p9100 at %lx:%lx\n", dp->full_name, - par->which_io, par->physbase); + par->which_io, info->fix.smem_start); return 0; @@ -319,7 +315,7 @@ out_dealloc_cmap: fb_dealloc_cmap(&info->cmap); out_unmap_screen: - of_iounmap(&op->resource[2], info->screen_base, par->fbsize); + of_iounmap(&op->resource[2], info->screen_base, info->fix.smem_len); out_unmap_regs: of_iounmap(&op->resource[0], par->regs, sizeof(struct p9100_regs)); @@ -340,7 +336,7 @@ static int __devexit p9100_remove(struct of_device *op) fb_dealloc_cmap(&info->cmap); of_iounmap(&op->resource[0], par->regs, sizeof(struct p9100_regs)); - of_iounmap(&op->resource[2], info->screen_base, par->fbsize); + of_iounmap(&op->resource[2], info->screen_base, info->fix.smem_len); framebuffer_release(info); diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index e00c1dff55d..c0af638fe70 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -32,25 +32,16 @@ #include <linux/init.h> #include <asm/abs_addr.h> +#include <asm/iommu.h> #include <asm/lv1call.h> #include <asm/ps3av.h> #include <asm/ps3fb.h> #include <asm/ps3.h> +#include <asm/ps3gpu.h> #define DEVICE_NAME "ps3fb" -#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x101 -#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x102 -#define L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP 0x600 -#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601 -#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT_SYNC 0x602 - -#define L1GPU_FB_BLIT_WAIT_FOR_COMPLETION (1ULL << 32) - -#define L1GPU_DISPLAY_SYNC_HSYNC 1 -#define L1GPU_DISPLAY_SYNC_VSYNC 2 - #define GPU_CMD_BUF_SIZE (2 * 1024 * 1024) #define GPU_FB_START (64 * 1024) #define GPU_IOIF (0x0d000000UL) @@ -462,33 +453,27 @@ static void ps3fb_sync_image(struct device *dev, u64 frame_offset, src_offset += GPU_FB_START; mutex_lock(&ps3_gpu_mutex); - status = lv1_gpu_context_attribute(ps3fb.context_handle, - L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, - dst_offset, GPU_IOIF + src_offset, - L1GPU_FB_BLIT_WAIT_FOR_COMPLETION | - (width << 16) | height, - line_length); + status = lv1_gpu_fb_blit(ps3fb.context_handle, dst_offset, + GPU_IOIF + src_offset, + L1GPU_FB_BLIT_WAIT_FOR_COMPLETION | + (width << 16) | height, + line_length); mutex_unlock(&ps3_gpu_mutex); if (status) - dev_err(dev, - "%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n", - __func__, status); + dev_err(dev, "%s: lv1_gpu_fb_blit failed: %d\n", __func__, + status); #ifdef HEAD_A - status = lv1_gpu_context_attribute(ps3fb.context_handle, - L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, - 0, frame_offset, 0, 0); + status = lv1_gpu_display_flip(ps3fb.context_handle, 0, frame_offset); if (status) - dev_err(dev, "%s: lv1_gpu_context_attribute FLIP failed: %d\n", - __func__, status); + dev_err(dev, "%s: lv1_gpu_display_flip failed: %d\n", __func__, + status); #endif #ifdef HEAD_B - status = lv1_gpu_context_attribute(ps3fb.context_handle, - L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, - 1, frame_offset, 0, 0); + status = lv1_gpu_display_flip(ps3fb.context_handle, 1, frame_offset); if (status) - dev_err(dev, "%s: lv1_gpu_context_attribute FLIP failed: %d\n", - __func__, status); + dev_err(dev, "%s: lv1_gpu_display_flip failed: %d\n", __func__, + status); #endif } @@ -956,73 +941,6 @@ static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr) } -static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, - struct device *dev) -{ - int error; - - dev_dbg(dev, "version_driver:%x\n", dinfo->version_driver); - dev_dbg(dev, "irq outlet:%x\n", dinfo->irq.irq_outlet); - dev_dbg(dev, - "version_gpu: %x memory_size: %x ch: %x core_freq: %d " - "mem_freq:%d\n", - dinfo->version_gpu, dinfo->memory_size, dinfo->hardware_channel, - dinfo->nvcore_frequency/1000000, dinfo->memory_frequency/1000000); - - if (dinfo->version_driver != GPU_DRIVER_INFO_VERSION) { - dev_err(dev, "%s: version_driver err:%x\n", __func__, - dinfo->version_driver); - return -EINVAL; - } - - error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet, - &ps3fb.irq_no); - if (error) { - dev_err(dev, "%s: ps3_alloc_irq failed %d\n", __func__, error); - return error; - } - - error = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, IRQF_DISABLED, - DEVICE_NAME, dev); - if (error) { - dev_err(dev, "%s: request_irq failed %d\n", __func__, error); - ps3_irq_plug_destroy(ps3fb.irq_no); - return error; - } - - dinfo->irq.mask = (1 << GPU_INTR_STATUS_VSYNC_1) | - (1 << GPU_INTR_STATUS_FLIP_1); - return 0; -} - -static int ps3fb_xdr_settings(u64 xdr_lpar, struct device *dev) -{ - int status; - - status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, - xdr_lpar, ps3fb_videomemory.size, 0); - if (status) { - dev_err(dev, "%s: lv1_gpu_context_iomap failed: %d\n", - __func__, status); - return -ENXIO; - } - dev_dbg(dev, "video:%p ioif:%lx lpar:%llx size:%lx\n", - ps3fb_videomemory.address, GPU_IOIF, xdr_lpar, - ps3fb_videomemory.size); - - status = lv1_gpu_context_attribute(ps3fb.context_handle, - L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP, - xdr_lpar, GPU_CMD_BUF_SIZE, - GPU_IOIF, 0); - if (status) { - dev_err(dev, - "%s: lv1_gpu_context_attribute FB_SETUP failed: %d\n", - __func__, status); - return -ENXIO; - } - return 0; -} - static struct fb_ops ps3fb_ops = { .fb_open = ps3fb_open, .fb_release = ps3fb_release, @@ -1048,49 +966,18 @@ static struct fb_fix_screeninfo ps3fb_fix __initdata = { .accel = FB_ACCEL_NONE, }; -static int ps3fb_set_sync(struct device *dev) -{ - int status; - -#ifdef HEAD_A - status = lv1_gpu_context_attribute(0x0, - L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, - 0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); - if (status) { - dev_err(dev, - "%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: " - "%d\n", - __func__, status); - return -1; - } -#endif -#ifdef HEAD_B - status = lv1_gpu_context_attribute(0x0, - L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, - 1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); - - if (status) { - dev_err(dev, - "%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: " - "%d\n", - __func__, status); - return -1; - } -#endif - return 0; -} - static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) { struct fb_info *info; struct ps3fb_par *par; - int retval = -ENOMEM; + int retval; u64 ddr_lpar = 0; u64 lpar_dma_control = 0; u64 lpar_driver_info = 0; u64 lpar_reports = 0; u64 lpar_reports_size = 0; u64 xdr_lpar; + struct gpu_driver_info *dinfo; void *fb_start; int status; struct task_struct *task; @@ -1101,8 +988,8 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) return -ENOMEM; } - status = ps3_open_hv_device(dev); - if (status) { + retval = ps3_open_hv_device(dev); + if (retval) { dev_err(&dev->core, "%s: ps3_open_hv_device failed\n", __func__); goto err; @@ -1116,7 +1003,24 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */ init_waitqueue_head(&ps3fb.wait_vsync); - ps3fb_set_sync(&dev->core); +#ifdef HEAD_A + status = lv1_gpu_display_sync(0x0, 0, L1GPU_DISPLAY_SYNC_VSYNC); + if (status) { + dev_err(&dev->core, "%s: lv1_gpu_display_sync failed: %d\n", + __func__, status); + retval = -ENODEV; + goto err_close_device; + } +#endif +#ifdef HEAD_B + status = lv1_gpu_display_sync(0x0, 1, L1GPU_DISPLAY_SYNC_VSYNC); + if (status) { + dev_err(&dev->core, "%s: lv1_gpu_display_sync failed: %d\n", + __func__, status); + retval = -ENODEV; + goto err_close_device; + } +#endif max_ps3fb_size = _ALIGN_UP(GPU_IOIF, 256*1024*1024) - GPU_IOIF; if (ps3fb_videomemory.size > max_ps3fb_size) { @@ -1131,7 +1035,7 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) if (status) { dev_err(&dev->core, "%s: lv1_gpu_memory_allocate failed: %d\n", __func__, status); - goto err; + goto err_close_device; } dev_dbg(&dev->core, "ddr:lpar:0x%llx\n", ddr_lpar); @@ -1141,33 +1045,85 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) &lpar_reports, &lpar_reports_size); if (status) { dev_err(&dev->core, - "%s: lv1_gpu_context_attribute failed: %d\n", __func__, + "%s: lv1_gpu_context_allocate failed: %d\n", __func__, status); goto err_gpu_memory_free; } /* vsync interrupt */ - ps3fb.dinfo = (void __force *)ioremap(lpar_driver_info, 128 * 1024); - if (!ps3fb.dinfo) { + dinfo = (void __force *)ioremap(lpar_driver_info, 128 * 1024); + if (!dinfo) { dev_err(&dev->core, "%s: ioremap failed\n", __func__); goto err_gpu_context_free; } - retval = ps3fb_vsync_settings(ps3fb.dinfo, &dev->core); - if (retval) + ps3fb.dinfo = dinfo; + dev_dbg(&dev->core, "version_driver:%x\n", dinfo->version_driver); + dev_dbg(&dev->core, "irq outlet:%x\n", dinfo->irq.irq_outlet); + dev_dbg(&dev->core, "version_gpu: %x memory_size: %x ch: %x " + "core_freq: %d mem_freq:%d\n", dinfo->version_gpu, + dinfo->memory_size, dinfo->hardware_channel, + dinfo->nvcore_frequency/1000000, + dinfo->memory_frequency/1000000); + + if (dinfo->version_driver != GPU_DRIVER_INFO_VERSION) { + dev_err(&dev->core, "%s: version_driver err:%x\n", __func__, + dinfo->version_driver); + retval = -EINVAL; + goto err_iounmap_dinfo; + } + + retval = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet, + &ps3fb.irq_no); + if (retval) { + dev_err(&dev->core, "%s: ps3_alloc_irq failed %d\n", __func__, + retval); goto err_iounmap_dinfo; + } + + retval = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, + IRQF_DISABLED, DEVICE_NAME, &dev->core); + if (retval) { + dev_err(&dev->core, "%s: request_irq failed %d\n", __func__, + retval); + goto err_destroy_plug; + } + + dinfo->irq.mask = (1 << GPU_INTR_STATUS_VSYNC_1) | + (1 << GPU_INTR_STATUS_FLIP_1); /* Clear memory to prevent kernel info leakage into userspace */ memset(ps3fb_videomemory.address, 0, ps3fb_videomemory.size); xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb_videomemory.address)); - retval = ps3fb_xdr_settings(xdr_lpar, &dev->core); - if (retval) + + status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, + xdr_lpar, ps3fb_videomemory.size, + CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | + CBE_IOPTE_M); + if (status) { + dev_err(&dev->core, "%s: lv1_gpu_context_iomap failed: %d\n", + __func__, status); + retval = -ENXIO; goto err_free_irq; + } + + dev_dbg(&dev->core, "video:%p ioif:%lx lpar:%llx size:%lx\n", + ps3fb_videomemory.address, GPU_IOIF, xdr_lpar, + ps3fb_videomemory.size); + + status = lv1_gpu_fb_setup(ps3fb.context_handle, xdr_lpar, + GPU_CMD_BUF_SIZE, GPU_IOIF); + if (status) { + dev_err(&dev->core, "%s: lv1_gpu_fb_setup failed: %d\n", + __func__, status); + retval = -ENXIO; + goto err_context_unmap; + } info = framebuffer_alloc(sizeof(struct ps3fb_par), &dev->core); if (!info) - goto err_free_irq; + goto err_context_fb_close; par = info->par; par->mode_id = ~ps3fb_mode; /* != ps3fb_mode, to trigger change */ @@ -1210,7 +1166,7 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) if (retval < 0) goto err_fb_dealloc; - dev->core.driver_data = info; + ps3_system_bus_set_drvdata(dev, info); dev_info(info->device, "%s %s, using %u KiB of video memory\n", dev_driver_string(info->dev), dev_name(info->dev), @@ -1232,8 +1188,14 @@ err_fb_dealloc: fb_dealloc_cmap(&info->cmap); err_framebuffer_release: framebuffer_release(info); +err_context_fb_close: + lv1_gpu_fb_close(ps3fb.context_handle); +err_context_unmap: + lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar, + ps3fb_videomemory.size, CBE_IOPTE_M); err_free_irq: free_irq(ps3fb.irq_no, &dev->core); +err_destroy_plug: ps3_irq_plug_destroy(ps3fb.irq_no); err_iounmap_dinfo: iounmap((u8 __force __iomem *)ps3fb.dinfo); @@ -1241,14 +1203,16 @@ err_gpu_context_free: lv1_gpu_context_free(ps3fb.context_handle); err_gpu_memory_free: lv1_gpu_memory_free(ps3fb.memory_handle); +err_close_device: + ps3_close_hv_device(dev); err: return retval; } static int ps3fb_shutdown(struct ps3_system_bus_device *dev) { - int status; - struct fb_info *info = dev->core.driver_data; + struct fb_info *info = ps3_system_bus_get_drvdata(dev); + u64 xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb_videomemory.address)); dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); @@ -1268,20 +1232,14 @@ static int ps3fb_shutdown(struct ps3_system_bus_device *dev) unregister_framebuffer(info); fb_dealloc_cmap(&info->cmap); framebuffer_release(info); - info = dev->core.driver_data = NULL; + ps3_system_bus_set_drvdata(dev, NULL); } iounmap((u8 __force __iomem *)ps3fb.dinfo); - - status = lv1_gpu_context_free(ps3fb.context_handle); - if (status) - dev_dbg(&dev->core, "lv1_gpu_context_free failed: %d\n", - status); - - status = lv1_gpu_memory_free(ps3fb.memory_handle); - if (status) - dev_dbg(&dev->core, "lv1_gpu_memory_free failed: %d\n", - status); - + lv1_gpu_fb_close(ps3fb.context_handle); + lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar, + ps3fb_videomemory.size, CBE_IOPTE_M); + lv1_gpu_context_free(ps3fb.context_handle); + lv1_gpu_memory_free(ps3fb.memory_handle); ps3_close_hv_device(dev); dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 89f231dc443..ff43c888502 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -1315,7 +1315,6 @@ static int __devinit tdfxfb_setup_i2c_bus(struct tdfxfb_i2c_chan *chan, strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name)); chan->adapter.owner = THIS_MODULE; - chan->adapter.class = I2C_CLASS_TV_ANALOG; chan->adapter.algo_data = &chan->algo; chan->adapter.dev.parent = dev; chan->algo.setsda = tdfxfb_i2c_setsda; diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index 2493f05e9f6..15502d5e364 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -384,7 +384,7 @@ static int __devinit xenfb_probe(struct xenbus_device *dev, fb_size = XENFB_DEFAULT_FB_LEN; } - dev->dev.driver_data = info; + dev_set_drvdata(&dev->dev, info); info->xbdev = dev; info->irq = -1; info->x1 = info->y1 = INT_MAX; @@ -503,7 +503,7 @@ xenfb_make_preferred_console(void) static int xenfb_resume(struct xenbus_device *dev) { - struct xenfb_info *info = dev->dev.driver_data; + struct xenfb_info *info = dev_get_drvdata(&dev->dev); xenfb_disconnect_backend(info); xenfb_init_shared_page(info, info->fb_info); @@ -512,7 +512,7 @@ static int xenfb_resume(struct xenbus_device *dev) static int xenfb_remove(struct xenbus_device *dev) { - struct xenfb_info *info = dev->dev.driver_data; + struct xenfb_info *info = dev_get_drvdata(&dev->dev); xenfb_disconnect_backend(info); if (info->fb_info) { @@ -621,7 +621,7 @@ static void xenfb_disconnect_backend(struct xenfb_info *info) static void xenfb_backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { - struct xenfb_info *info = dev->dev.driver_data; + struct xenfb_info *info = dev_get_drvdata(&dev->dev); int val; switch (backend_state) { |