diff options
Diffstat (limited to 'drivers')
192 files changed, 12791 insertions, 5395 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 26ca9031ea49..adad2f3d438a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_FC4) += fc4/ obj-$(CONFIG_SCSI) += scsi/ obj-$(CONFIG_ATA) += ata/ obj-$(CONFIG_FUSION) += message/ +obj-$(CONFIG_FIREWIRE) += firewire/ obj-$(CONFIG_IEEE1394) += ieee1394/ obj-y += cdrom/ obj-y += auxdisplay/ diff --git a/drivers/acpi/dispatcher/dsmethod.c b/drivers/acpi/dispatcher/dsmethod.c index 1683e5c5b94c..1cbe61905824 100644 --- a/drivers/acpi/dispatcher/dsmethod.c +++ b/drivers/acpi/dispatcher/dsmethod.c @@ -231,8 +231,10 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node, * Obtain the method mutex if necessary. Do not acquire mutex for a * recursive call. */ - if (acpi_os_get_thread_id() != - obj_desc->method.mutex->mutex.owner_thread_id) { + if (!walk_state || + !obj_desc->method.mutex->mutex.owner_thread || + (walk_state->thread != + obj_desc->method.mutex->mutex.owner_thread)) { /* * Acquire the method mutex. This releases the interpreter if we * block (and reacquires it before it returns) @@ -246,14 +248,14 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node, } /* Update the mutex and walk info and save the original sync_level */ - obj_desc->method.mutex->mutex.owner_thread_id = - acpi_os_get_thread_id(); if (walk_state) { obj_desc->method.mutex->mutex. original_sync_level = walk_state->thread->current_sync_level; + obj_desc->method.mutex->mutex.owner_thread = + walk_state->thread; walk_state->thread->current_sync_level = obj_desc->method.sync_level; } else { @@ -567,7 +569,7 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, acpi_os_release_mutex(method_desc->method.mutex->mutex. os_mutex); - method_desc->method.mutex->mutex.owner_thread_id = ACPI_MUTEX_NOT_ACQUIRED; + method_desc->method.mutex->mutex.owner_thread = NULL; } } diff --git a/drivers/acpi/dispatcher/dsopcode.c b/drivers/acpi/dispatcher/dsopcode.c index 6c6104a7a247..fc9da4879cbf 100644 --- a/drivers/acpi/dispatcher/dsopcode.c +++ b/drivers/acpi/dispatcher/dsopcode.c @@ -866,8 +866,7 @@ acpi_ds_eval_data_object_operands(struct acpi_walk_state *walk_state, ((op->common.parent->common.aml_opcode != AML_PACKAGE_OP) && (op->common.parent->common.aml_opcode != AML_VAR_PACKAGE_OP) - && (op->common.parent->common.aml_opcode != - AML_NAME_OP))) { + && (op->common.parent->common.aml_opcode != AML_NAME_OP))) { walk_state->result_obj = obj_desc; } } diff --git a/drivers/acpi/dispatcher/dsutils.c b/drivers/acpi/dispatcher/dsutils.c index e4073e05a75c..71503c036f7c 100644 --- a/drivers/acpi/dispatcher/dsutils.c +++ b/drivers/acpi/dispatcher/dsutils.c @@ -556,10 +556,9 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state, * indicate this to the interpreter, set the * object to the root */ - obj_desc = - ACPI_CAST_PTR(union - acpi_operand_object, - acpi_gbl_root_node); + obj_desc = ACPI_CAST_PTR(union + acpi_operand_object, + acpi_gbl_root_node); status = AE_OK; } else { /* diff --git a/drivers/acpi/dispatcher/dswstate.c b/drivers/acpi/dispatcher/dswstate.c index 16c8e38b51ef..5afcdd9c7449 100644 --- a/drivers/acpi/dispatcher/dswstate.c +++ b/drivers/acpi/dispatcher/dswstate.c @@ -630,12 +630,9 @@ struct acpi_walk_state *acpi_ds_pop_walk_state(struct acpi_thread_state *thread) * ******************************************************************************/ -struct acpi_walk_state *acpi_ds_create_walk_state(acpi_owner_id owner_id, - union acpi_parse_object - *origin, - union acpi_operand_object - *method_desc, - struct acpi_thread_state +struct acpi_walk_state *acpi_ds_create_walk_state(acpi_owner_id owner_id, union acpi_parse_object + *origin, union acpi_operand_object + *method_desc, struct acpi_thread_state *thread) { struct acpi_walk_state *walk_state; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e08cf98f504f..82f496c07675 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -147,9 +147,10 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event, return 0; } -static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, unsigned count) +static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, + unsigned count, int force_poll) { - if (acpi_ec_mode == EC_POLL) { + if (unlikely(force_poll) || acpi_ec_mode == EC_POLL) { unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); while (time_before(jiffies, delay)) { if (acpi_ec_check_status(ec, event, 0)) @@ -173,14 +174,15 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, unsigned count) static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, const u8 * wdata, unsigned wdata_len, - u8 * rdata, unsigned rdata_len) + u8 * rdata, unsigned rdata_len, + int force_poll) { int result = 0; unsigned count = atomic_read(&ec->event_count); acpi_ec_write_cmd(ec, command); for (; wdata_len > 0; --wdata_len) { - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count, force_poll); if (result) { printk(KERN_ERR PREFIX "write_cmd timeout, command = %d\n", command); @@ -191,7 +193,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, } if (!rdata_len) { - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count, force_poll); if (result) { printk(KERN_ERR PREFIX "finish-write timeout, command = %d\n", command); @@ -202,7 +204,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, } for (; rdata_len > 0; --rdata_len) { - result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1, count); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1, count, force_poll); if (result) { printk(KERN_ERR PREFIX "read timeout, command = %d\n", command); @@ -217,7 +219,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, const u8 * wdata, unsigned wdata_len, - u8 * rdata, unsigned rdata_len) + u8 * rdata, unsigned rdata_len, + int force_poll) { int status; u32 glk; @@ -240,7 +243,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, /* Make sure GPE is enabled before doing transaction */ acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, 0); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, 0, 0); if (status) { printk(KERN_DEBUG PREFIX "input buffer is not empty, aborting transaction\n"); @@ -249,7 +252,8 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, status = acpi_ec_transaction_unlocked(ec, command, wdata, wdata_len, - rdata, rdata_len); + rdata, rdata_len, + force_poll); end: @@ -267,12 +271,12 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, int acpi_ec_burst_enable(struct acpi_ec *ec) { u8 d; - return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1); + return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1, 0); } int acpi_ec_burst_disable(struct acpi_ec *ec) { - return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0); + return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0, 0); } static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) @@ -281,7 +285,7 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) u8 d; result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_READ, - &address, 1, &d, 1); + &address, 1, &d, 1, 0); *data = d; return result; } @@ -290,7 +294,7 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) { u8 wdata[2] = { address, data }; return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE, - wdata, 2, NULL, 0); + wdata, 2, NULL, 0, 0); } /* @@ -349,13 +353,15 @@ EXPORT_SYMBOL(ec_write); int ec_transaction(u8 command, const u8 * wdata, unsigned wdata_len, - u8 * rdata, unsigned rdata_len) + u8 * rdata, unsigned rdata_len, + int force_poll) { if (!first_ec) return -ENODEV; return acpi_ec_transaction(first_ec, command, wdata, - wdata_len, rdata, rdata_len); + wdata_len, rdata, rdata_len, + force_poll); } EXPORT_SYMBOL(ec_transaction); @@ -374,7 +380,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) * bit to be cleared (and thus clearing the interrupt source). */ - result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_QUERY, NULL, 0, &d, 1); + result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_QUERY, NULL, 0, &d, 1, 0); if (result) return result; @@ -410,6 +416,7 @@ static u32 acpi_ec_gpe_handler(void *data) acpi_status status = AE_OK; u8 value; struct acpi_ec *ec = data; + atomic_inc(&ec->event_count); if (acpi_ec_mode == EC_INTR) { diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/events/evgpe.c index 635ba449ebc2..e22f4a973c0f 100644 --- a/drivers/acpi/events/evgpe.c +++ b/drivers/acpi/events/evgpe.c @@ -341,9 +341,8 @@ struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, /* A Non-NULL gpe_device means this is a GPE Block Device */ - obj_desc = - acpi_ns_get_attached_object((struct acpi_namespace_node *) - gpe_device); + obj_desc = acpi_ns_get_attached_object((struct acpi_namespace_node *) + gpe_device); if (!obj_desc || !obj_desc->device.gpe_block) { return (NULL); } diff --git a/drivers/acpi/events/evgpeblk.c b/drivers/acpi/events/evgpeblk.c index ad5bc76edf46..902c287b3a4f 100644 --- a/drivers/acpi/events/evgpeblk.c +++ b/drivers/acpi/events/evgpeblk.c @@ -1033,8 +1033,7 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_METHOD) - && (gpe_event_info-> - flags & ACPI_GPE_TYPE_RUNTIME)) { + && (gpe_event_info->flags & ACPI_GPE_TYPE_RUNTIME)) { gpe_enabled_count++; } diff --git a/drivers/acpi/events/evmisc.c b/drivers/acpi/events/evmisc.c index cae786ca8600..21cb749d0c75 100644 --- a/drivers/acpi/events/evmisc.c +++ b/drivers/acpi/events/evmisc.c @@ -196,15 +196,12 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node, notify_info->notify.value = (u16) notify_value; notify_info->notify.handler_obj = handler_obj; - acpi_ex_exit_interpreter(); - - acpi_ev_notify_dispatch(notify_info); - - status = acpi_ex_enter_interpreter(); + status = + acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ev_notify_dispatch, + notify_info); if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); + acpi_ut_delete_generic_state(notify_info); } - } if (!handler_obj) { @@ -323,8 +320,9 @@ static u32 acpi_ev_global_lock_handler(void *context) acpi_gbl_global_lock_acquired = TRUE; /* Send a unit to the semaphore */ - if (ACPI_FAILURE(acpi_os_signal_semaphore( - acpi_gbl_global_lock_semaphore, 1))) { + if (ACPI_FAILURE + (acpi_os_signal_semaphore + (acpi_gbl_global_lock_semaphore, 1))) { ACPI_ERROR((AE_INFO, "Could not signal Global Lock semaphore")); } @@ -450,7 +448,9 @@ acpi_status acpi_ev_acquire_global_lock(u16 timeout) } if (ACPI_FAILURE(status)) { - status = acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex, timeout); + status = + acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex, + timeout); } if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); diff --git a/drivers/acpi/events/evregion.c b/drivers/acpi/events/evregion.c index 96b0e8431748..e99f0c435a47 100644 --- a/drivers/acpi/events/evregion.c +++ b/drivers/acpi/events/evregion.c @@ -291,7 +291,6 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, u32 bit_width, acpi_integer * value) { acpi_status status; - acpi_status status2; acpi_adr_space_handler handler; acpi_adr_space_setup region_setup; union acpi_operand_object *handler_desc; @@ -345,7 +344,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, * setup will potentially execute control methods * (e.g., _REG method for this region) */ - acpi_ex_exit_interpreter(); + acpi_ex_relinquish_interpreter(); status = region_setup(region_obj, ACPI_REGION_ACTIVATE, handler_desc->address_space.context, @@ -353,10 +352,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, /* Re-enter the interpreter */ - status2 = acpi_ex_enter_interpreter(); - if (ACPI_FAILURE(status2)) { - return_ACPI_STATUS(status2); - } + acpi_ex_reacquire_interpreter(); /* Check for failure of the Region Setup */ @@ -409,7 +405,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, * exit the interpreter because the handler *might* block -- we don't * know what it will do, so we can't hold the lock on the intepreter. */ - acpi_ex_exit_interpreter(); + acpi_ex_relinquish_interpreter(); } /* Call the handler */ @@ -430,10 +426,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, * We just returned from a non-default handler, we must re-enter the * interpreter */ - status2 = acpi_ex_enter_interpreter(); - if (ACPI_FAILURE(status2)) { - return_ACPI_STATUS(status2); - } + acpi_ex_reacquire_interpreter(); } return_ACPI_STATUS(status); diff --git a/drivers/acpi/events/evrgnini.c b/drivers/acpi/events/evrgnini.c index a4fa7e6822a3..400d90fca966 100644 --- a/drivers/acpi/events/evrgnini.c +++ b/drivers/acpi/events/evrgnini.c @@ -228,7 +228,8 @@ acpi_ev_pci_config_region_setup(acpi_handle handle, /* Install a handler for this PCI root bridge */ - status = acpi_install_address_space_handler((acpi_handle) pci_root_node, ACPI_ADR_SPACE_PCI_CONFIG, ACPI_DEFAULT_HANDLER, NULL, NULL); + status = + acpi_install_address_space_handler((acpi_handle) pci_root_node, ACPI_ADR_SPACE_PCI_CONFIG, ACPI_DEFAULT_HANDLER, NULL, NULL); if (ACPI_FAILURE(status)) { if (status == AE_SAME_HANDLER) { /* diff --git a/drivers/acpi/events/evxface.c b/drivers/acpi/events/evxface.c index a3379bafa676..6d866a01f5f4 100644 --- a/drivers/acpi/events/evxface.c +++ b/drivers/acpi/events/evxface.c @@ -91,7 +91,6 @@ acpi_status acpi_install_exception_handler(acpi_exception_handler handler) ACPI_EXPORT_SYMBOL(acpi_install_exception_handler) #endif /* ACPI_FUTURE_USAGE */ - /******************************************************************************* * * FUNCTION: acpi_install_fixed_event_handler @@ -768,11 +767,9 @@ acpi_status acpi_acquire_global_lock(u16 timeout, u32 * handle) return (AE_BAD_PARAMETER); } - status = acpi_ex_enter_interpreter(); - if (ACPI_FAILURE(status)) { - return (status); - } + /* Must lock interpreter to prevent race conditions */ + acpi_ex_enter_interpreter(); status = acpi_ev_acquire_global_lock(timeout); acpi_ex_exit_interpreter(); diff --git a/drivers/acpi/events/evxfevnt.c b/drivers/acpi/events/evxfevnt.c index 17065e98807c..9cbd3414a574 100644 --- a/drivers/acpi/events/evxfevnt.c +++ b/drivers/acpi/events/evxfevnt.c @@ -472,7 +472,6 @@ acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags) } ACPI_EXPORT_SYMBOL(acpi_clear_gpe) - #ifdef ACPI_FUTURE_USAGE /******************************************************************************* * @@ -568,7 +567,6 @@ acpi_get_gpe_status(acpi_handle gpe_device, ACPI_EXPORT_SYMBOL(acpi_get_gpe_status) #endif /* ACPI_FUTURE_USAGE */ - /******************************************************************************* * * FUNCTION: acpi_install_gpe_block diff --git a/drivers/acpi/executer/exconvrt.c b/drivers/acpi/executer/exconvrt.c index d470e8b1f4ea..79f2c0d42c06 100644 --- a/drivers/acpi/executer/exconvrt.c +++ b/drivers/acpi/executer/exconvrt.c @@ -512,9 +512,8 @@ acpi_ex_convert_to_string(union acpi_operand_object * obj_desc, * Create a new string object and string buffer * (-1 because of extra separator included in string_length from above) */ - return_desc = - acpi_ut_create_string_object((acpi_size) - (string_length - 1)); + return_desc = acpi_ut_create_string_object((acpi_size) + (string_length - 1)); if (!return_desc) { return_ACPI_STATUS(AE_NO_MEMORY); } diff --git a/drivers/acpi/executer/excreate.c b/drivers/acpi/executer/excreate.c index ae97812681a3..6e9a23e47fef 100644 --- a/drivers/acpi/executer/excreate.c +++ b/drivers/acpi/executer/excreate.c @@ -50,7 +50,6 @@ #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("excreate") - #ifndef ACPI_NO_METHOD_EXECUTION /******************************************************************************* * @@ -583,10 +582,7 @@ acpi_ex_create_method(u8 * aml_start, * Get the sync_level. If method is serialized, a mutex will be * created for this method when it is parsed. */ - if (acpi_gbl_all_methods_serialized) { - obj_desc->method.sync_level = 0; - obj_desc->method.method_flags |= AML_METHOD_SERIALIZED; - } else if (method_flags & AML_METHOD_SERIALIZED) { + if (method_flags & AML_METHOD_SERIALIZED) { /* * ACPI 1.0: sync_level = 0 * ACPI 2.0: sync_level = sync_level in method declaration diff --git a/drivers/acpi/executer/exdump.c b/drivers/acpi/executer/exdump.c index 1a73c14df2c5..51c9c29987c3 100644 --- a/drivers/acpi/executer/exdump.c +++ b/drivers/acpi/executer/exdump.c @@ -134,7 +134,7 @@ static struct acpi_exdump_info acpi_ex_dump_method[8] = { static struct acpi_exdump_info acpi_ex_dump_mutex[5] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_mutex), NULL}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(mutex.sync_level), "Sync Level"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.owner_thread_id), "Owner Thread"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.owner_thread), "Owner Thread"}, {ACPI_EXD_UINT16, ACPI_EXD_OFFSET(mutex.acquisition_depth), "Acquire Depth"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.os_mutex), "OsMutex"} @@ -451,9 +451,8 @@ void acpi_ex_dump_operand(union acpi_operand_object *obj_desc, u32 depth) ACPI_FUNCTION_NAME(ex_dump_operand) - if (! - ((ACPI_LV_EXEC & acpi_dbg_level) - && (_COMPONENT & acpi_dbg_layer))) { + if (!((ACPI_LV_EXEC & acpi_dbg_level) + && (_COMPONENT & acpi_dbg_layer))) { return; } @@ -844,9 +843,8 @@ void acpi_ex_dump_namespace_node(struct acpi_namespace_node *node, u32 flags) ACPI_FUNCTION_ENTRY(); if (!flags) { - if (! - ((ACPI_LV_OBJECTS & acpi_dbg_level) - && (_COMPONENT & acpi_dbg_layer))) { + if (!((ACPI_LV_OBJECTS & acpi_dbg_level) + && (_COMPONENT & acpi_dbg_layer))) { return; } } @@ -1011,9 +1009,8 @@ acpi_ex_dump_object_descriptor(union acpi_operand_object *obj_desc, u32 flags) } if (!flags) { - if (! - ((ACPI_LV_OBJECTS & acpi_dbg_level) - && (_COMPONENT & acpi_dbg_layer))) { + if (!((ACPI_LV_OBJECTS & acpi_dbg_level) + && (_COMPONENT & acpi_dbg_layer))) { return_VOID; } } diff --git a/drivers/acpi/executer/exmutex.c b/drivers/acpi/executer/exmutex.c index 4eb883bda6ae..6748e3ef0997 100644 --- a/drivers/acpi/executer/exmutex.c +++ b/drivers/acpi/executer/exmutex.c @@ -66,9 +66,10 @@ acpi_ex_link_mutex(union acpi_operand_object *obj_desc, * ******************************************************************************/ -void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc, - struct acpi_thread_state *thread) +void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc) { + struct acpi_thread_state *thread = obj_desc->mutex.owner_thread; + if (!thread) { return; } @@ -173,13 +174,16 @@ acpi_ex_acquire_mutex(union acpi_operand_object *time_desc, /* Support for multiple acquires by the owning thread */ - if (obj_desc->mutex.owner_thread_id == acpi_os_get_thread_id()) { - /* - * The mutex is already owned by this thread, just increment the - * acquisition depth - */ - obj_desc->mutex.acquisition_depth++; - return_ACPI_STATUS(AE_OK); + if (obj_desc->mutex.owner_thread) { + if (obj_desc->mutex.owner_thread->thread_id == + walk_state->thread->thread_id) { + /* + * The mutex is already owned by this thread, just increment the + * acquisition depth + */ + obj_desc->mutex.acquisition_depth++; + return_ACPI_STATUS(AE_OK); + } } /* Acquire the mutex, wait if necessary. Special case for Global Lock */ @@ -202,7 +206,7 @@ acpi_ex_acquire_mutex(union acpi_operand_object *time_desc, /* Have the mutex: update mutex and walk info and save the sync_level */ - obj_desc->mutex.owner_thread_id = acpi_os_get_thread_id(); + obj_desc->mutex.owner_thread = walk_state->thread; obj_desc->mutex.acquisition_depth = 1; obj_desc->mutex.original_sync_level = walk_state->thread->current_sync_level; @@ -242,7 +246,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, /* The mutex must have been previously acquired in order to release it */ - if (!obj_desc->mutex.owner_thread_id) { + if (!obj_desc->mutex.owner_thread) { ACPI_ERROR((AE_INFO, "Cannot release Mutex [%4.4s], not acquired", acpi_ut_get_node_name(obj_desc->mutex.node))); @@ -262,14 +266,15 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, * The Mutex is owned, but this thread must be the owner. * Special case for Global Lock, any thread can release */ - if ((obj_desc->mutex.owner_thread_id != + if ((obj_desc->mutex.owner_thread->thread_id != walk_state->thread->thread_id) && (obj_desc->mutex.os_mutex != 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, acpi_ut_get_node_name(obj_desc->mutex.node), - (unsigned long)obj_desc->mutex.owner_thread_id)); + (unsigned long)obj_desc->mutex.owner_thread-> + thread_id)); return_ACPI_STATUS(AE_AML_NOT_OWNER); } @@ -296,7 +301,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, /* Unlink the mutex from the owner's list */ - acpi_ex_unlink_mutex(obj_desc, walk_state->thread); + acpi_ex_unlink_mutex(obj_desc); /* Release the mutex, special case for Global Lock */ @@ -308,7 +313,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, /* Update the mutex and restore sync_level */ - obj_desc->mutex.owner_thread_id = ACPI_MUTEX_NOT_ACQUIRED; + obj_desc->mutex.owner_thread = NULL; walk_state->thread->current_sync_level = obj_desc->mutex.original_sync_level; @@ -363,7 +368,7 @@ void acpi_ex_release_all_mutexes(struct acpi_thread_state *thread) /* Mark mutex unowned */ - obj_desc->mutex.owner_thread_id = ACPI_MUTEX_NOT_ACQUIRED; + obj_desc->mutex.owner_thread = NULL; /* Update Thread sync_level (Last mutex is the important one) */ diff --git a/drivers/acpi/executer/exnames.c b/drivers/acpi/executer/exnames.c index 1ee4fb1175c6..308eae52dc05 100644 --- a/drivers/acpi/executer/exnames.c +++ b/drivers/acpi/executer/exnames.c @@ -177,8 +177,7 @@ static acpi_status acpi_ex_name_segment(u8 ** in_aml_address, char *name_string) ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "Bytes from stream:\n")); - for (index = 0; - (index < ACPI_NAME_SIZE) + for (index = 0; (index < ACPI_NAME_SIZE) && (acpi_ut_valid_acpi_char(*aml_address, 0)); index++) { char_buf[index] = *aml_address++; ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "%c\n", char_buf[index])); diff --git a/drivers/acpi/executer/exprep.c b/drivers/acpi/executer/exprep.c index a6696621ff1b..efe5d4b461a4 100644 --- a/drivers/acpi/executer/exprep.c +++ b/drivers/acpi/executer/exprep.c @@ -242,7 +242,7 @@ acpi_ex_decode_field_access(union acpi_operand_object *obj_desc, obj_desc->common_field.bit_length, 0xFFFFFFFF /* Temp until we pass region_length as parameter */ - ); + ); bit_length = byte_alignment * 8; #endif diff --git a/drivers/acpi/executer/exresop.c b/drivers/acpi/executer/exresop.c index ba761862a599..09d897b3f6d5 100644 --- a/drivers/acpi/executer/exresop.c +++ b/drivers/acpi/executer/exresop.c @@ -354,8 +354,7 @@ acpi_ex_resolve_operands(u16 opcode, if ((opcode == AML_STORE_OP) && (ACPI_GET_OBJECT_TYPE(*stack_ptr) == ACPI_TYPE_LOCAL_REFERENCE) - && ((*stack_ptr)->reference.opcode == - AML_INDEX_OP)) { + && ((*stack_ptr)->reference.opcode == AML_INDEX_OP)) { goto next_operand; } break; diff --git a/drivers/acpi/executer/exsystem.c b/drivers/acpi/executer/exsystem.c index b2edf620ba89..9460baff3032 100644 --- a/drivers/acpi/executer/exsystem.c +++ b/drivers/acpi/executer/exsystem.c @@ -66,7 +66,6 @@ ACPI_MODULE_NAME("exsystem") acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout) { acpi_status status; - acpi_status status2; ACPI_FUNCTION_TRACE(ex_system_wait_semaphore); @@ -79,7 +78,7 @@ acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout) /* We must wait, so unlock the interpreter */ - acpi_ex_exit_interpreter(); + acpi_ex_relinquish_interpreter(); status = acpi_os_wait_semaphore(semaphore, 1, timeout); @@ -89,13 +88,7 @@ acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout) /* Reacquire the interpreter */ - status2 = acpi_ex_enter_interpreter(); - if (ACPI_FAILURE(status2)) { - - /* Report fatal error, could not acquire interpreter */ - - return_ACPI_STATUS(status2); - } + acpi_ex_reacquire_interpreter(); } return_ACPI_STATUS(status); @@ -119,7 +112,6 @@ acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout) acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout) { acpi_status status; - acpi_status status2; ACPI_FUNCTION_TRACE(ex_system_wait_mutex); @@ -132,7 +124,7 @@ acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout) /* We must wait, so unlock the interpreter */ - acpi_ex_exit_interpreter(); + acpi_ex_relinquish_interpreter(); status = acpi_os_acquire_mutex(mutex, timeout); @@ -142,13 +134,7 @@ acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout) /* Reacquire the interpreter */ - status2 = acpi_ex_enter_interpreter(); - if (ACPI_FAILURE(status2)) { - - /* Report fatal error, could not acquire interpreter */ - - return_ACPI_STATUS(status2); - } + acpi_ex_reacquire_interpreter(); } return_ACPI_STATUS(status); @@ -209,20 +195,18 @@ acpi_status acpi_ex_system_do_stall(u32 how_long) acpi_status acpi_ex_system_do_suspend(acpi_integer how_long) { - acpi_status status; - ACPI_FUNCTION_ENTRY(); /* Since this thread will sleep, we must release the interpreter */ - acpi_ex_exit_interpreter(); + acpi_ex_relinquish_interpreter(); acpi_os_sleep(how_long); /* And now we must get the interpreter again */ - status = acpi_ex_enter_interpreter(); - return (status); + acpi_ex_reacquire_interpreter(); + return (AE_OK); } /******************************************************************************* diff --git a/drivers/acpi/executer/exutils.c b/drivers/acpi/executer/exutils.c index aea461f3a48c..6b0aeccbb69b 100644 --- a/drivers/acpi/executer/exutils.c +++ b/drivers/acpi/executer/exutils.c @@ -76,14 +76,15 @@ static u32 acpi_ex_digits_needed(acpi_integer value, u32 base); * * PARAMETERS: None * - * RETURN: Status + * RETURN: None * - * DESCRIPTION: Enter the interpreter execution region. Failure to enter - * the interpreter region is a fatal system error + * DESCRIPTION: Enter the interpreter execution region. Failure to enter + * the interpreter region is a fatal system error. Used in + * conjunction with exit_interpreter. * ******************************************************************************/ -acpi_status acpi_ex_enter_interpreter(void) +void acpi_ex_enter_interpreter(void) { acpi_status status; @@ -91,31 +92,55 @@ acpi_status acpi_ex_enter_interpreter(void) status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER); if (ACPI_FAILURE(status)) { - ACPI_ERROR((AE_INFO, "Could not acquire interpreter mutex")); + ACPI_ERROR((AE_INFO, + "Could not acquire AML Interpreter mutex")); } - return_ACPI_STATUS(status); + return_VOID; } /******************************************************************************* * - * FUNCTION: acpi_ex_exit_interpreter + * FUNCTION: acpi_ex_reacquire_interpreter * * PARAMETERS: None * * RETURN: None * - * DESCRIPTION: Exit the interpreter execution region + * DESCRIPTION: Reacquire the interpreter execution region from within the + * interpreter code. Failure to enter the interpreter region is a + * fatal system error. Used in conjuction with + * relinquish_interpreter + * + ******************************************************************************/ + +void acpi_ex_reacquire_interpreter(void) +{ + ACPI_FUNCTION_TRACE(ex_reacquire_interpreter); + + /* + * If the global serialized flag is set, do not release the interpreter, + * since it was not actually released by acpi_ex_relinquish_interpreter. + * This forces the interpreter to be single threaded. + */ + if (!acpi_gbl_all_methods_serialized) { + acpi_ex_enter_interpreter(); + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_exit_interpreter + * + * PARAMETERS: None + * + * RETURN: None * - * Cases where the interpreter is unlocked: - * 1) Completion of the execution of a control method - * 2) Method blocked on a Sleep() AML opcode - * 3) Method blocked on an Acquire() AML opcode - * 4) Method blocked on a Wait() AML opcode - * 5) Method blocked to acquire the global lock - * 6) Method blocked to execute a serialized control method that is - * already executing - * 7) About to invoke a user-installed opregion handler + * DESCRIPTION: Exit the interpreter execution region. This is the top level + * routine used to exit the interpreter when all processing has + * been completed. * ******************************************************************************/ @@ -127,7 +152,46 @@ void acpi_ex_exit_interpreter(void) status = acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); if (ACPI_FAILURE(status)) { - ACPI_ERROR((AE_INFO, "Could not release interpreter mutex")); + ACPI_ERROR((AE_INFO, + "Could not release AML Interpreter mutex")); + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_relinquish_interpreter + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Exit the interpreter execution region, from within the + * interpreter - before attempting an operation that will possibly + * block the running thread. + * + * Cases where the interpreter is unlocked internally + * 1) Method to be blocked on a Sleep() AML opcode + * 2) Method to be blocked on an Acquire() AML opcode + * 3) Method to be blocked on a Wait() AML opcode + * 4) Method to be blocked to acquire the global lock + * 5) Method to be blocked waiting to execute a serialized control method + * that is currently executing + * 6) About to invoke a user-installed opregion handler + * + ******************************************************************************/ + +void acpi_ex_relinquish_interpreter(void) +{ + ACPI_FUNCTION_TRACE(ex_relinquish_interpreter); + + /* + * If the global serialized flag is set, do not release the interpreter. + * This forces the interpreter to be single threaded. + */ + if (!acpi_gbl_all_methods_serialized) { + acpi_ex_exit_interpreter(); } return_VOID; @@ -141,8 +205,8 @@ void acpi_ex_exit_interpreter(void) * * RETURN: none * - * DESCRIPTION: Truncate a number to 32-bits if the currently executing method - * belongs to a 32-bit ACPI table. + * DESCRIPTION: Truncate an ACPI Integer to 32 bits if the execution mode is + * 32-bit, as determined by the revision of the DSDT. * ******************************************************************************/ diff --git a/drivers/acpi/hardware/hwsleep.c b/drivers/acpi/hardware/hwsleep.c index c84b1faba28c..76c525dc590b 100644 --- a/drivers/acpi/hardware/hwsleep.c +++ b/drivers/acpi/hardware/hwsleep.c @@ -152,7 +152,6 @@ acpi_get_firmware_waking_vector(acpi_physical_address * physical_address) ACPI_EXPORT_SYMBOL(acpi_get_firmware_waking_vector) #endif - /******************************************************************************* * * FUNCTION: acpi_enter_sleep_state_prep diff --git a/drivers/acpi/namespace/nseval.c b/drivers/acpi/namespace/nseval.c index 26fd0dd6953d..97b2ac57c16b 100644 --- a/drivers/acpi/namespace/nseval.c +++ b/drivers/acpi/namespace/nseval.c @@ -75,7 +75,7 @@ ACPI_MODULE_NAME("nseval") * MUTEX: Locks interpreter * ******************************************************************************/ -acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info) +acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info) { acpi_status status; @@ -154,11 +154,7 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info) * Execute the method via the interpreter. The interpreter is locked * here before calling into the AML parser */ - status = acpi_ex_enter_interpreter(); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - + acpi_ex_enter_interpreter(); status = acpi_ps_execute_method(info); acpi_ex_exit_interpreter(); } else { @@ -182,10 +178,7 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info) * resolution, we must lock it because we could access an opregion. * The opregion access code assumes that the interpreter is locked. */ - status = acpi_ex_enter_interpreter(); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } + acpi_ex_enter_interpreter(); /* Function has a strange interface */ diff --git a/drivers/acpi/namespace/nsinit.c b/drivers/acpi/namespace/nsinit.c index c4ab615f77fe..33db2241044e 100644 --- a/drivers/acpi/namespace/nsinit.c +++ b/drivers/acpi/namespace/nsinit.c @@ -214,7 +214,7 @@ acpi_ns_init_one_object(acpi_handle obj_handle, u32 level, void *context, void **return_value) { acpi_object_type type; - acpi_status status; + acpi_status status = AE_OK; struct acpi_init_walk_info *info = (struct acpi_init_walk_info *)context; struct acpi_namespace_node *node = @@ -268,10 +268,7 @@ acpi_ns_init_one_object(acpi_handle obj_handle, /* * Must lock the interpreter before executing AML code */ - status = acpi_ex_enter_interpreter(); - if (ACPI_FAILURE(status)) { - return (status); - } + acpi_ex_enter_interpreter(); /* * Each of these types can contain executable AML code within the diff --git a/drivers/acpi/namespace/nswalk.c b/drivers/acpi/namespace/nswalk.c index 94eb8f332d94..280b8357c46c 100644 --- a/drivers/acpi/namespace/nswalk.c +++ b/drivers/acpi/namespace/nswalk.c @@ -65,10 +65,8 @@ 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(acpi_object_type type, struct acpi_namespace_node + *parent_node, struct acpi_namespace_node *child_node) { struct acpi_namespace_node *next_node = NULL; diff --git a/drivers/acpi/namespace/nsxfeval.c b/drivers/acpi/namespace/nsxfeval.c index 8904d0fae6a2..be4f2899de74 100644 --- a/drivers/acpi/namespace/nsxfeval.c +++ b/drivers/acpi/namespace/nsxfeval.c @@ -48,7 +48,6 @@ #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsxfeval") - #ifdef ACPI_FUTURE_USAGE /******************************************************************************* * @@ -73,8 +72,8 @@ ACPI_MODULE_NAME("nsxfeval") acpi_status acpi_evaluate_object_typed(acpi_handle handle, acpi_string pathname, - struct acpi_object_list * external_params, - struct acpi_buffer * return_buffer, + struct acpi_object_list *external_params, + struct acpi_buffer *return_buffer, acpi_object_type return_type) { acpi_status status; @@ -143,7 +142,6 @@ acpi_evaluate_object_typed(acpi_handle handle, ACPI_EXPORT_SYMBOL(acpi_evaluate_object_typed) #endif /* ACPI_FUTURE_USAGE */ - /******************************************************************************* * * FUNCTION: acpi_evaluate_object @@ -170,7 +168,6 @@ acpi_evaluate_object(acpi_handle handle, struct acpi_buffer *return_buffer) { acpi_status status; - acpi_status status2; struct acpi_evaluate_info *info; acpi_size buffer_space_needed; u32 i; @@ -329,14 +326,12 @@ acpi_evaluate_object(acpi_handle handle, * Delete the internal return object. NOTE: Interpreter must be * locked to avoid race condition. */ - status2 = acpi_ex_enter_interpreter(); - if (ACPI_SUCCESS(status2)) { + acpi_ex_enter_interpreter(); - /* Remove one reference on the return object (should delete it) */ + /* Remove one reference on the return object (should delete it) */ - acpi_ut_remove_reference(info->return_object); - acpi_ex_exit_interpreter(); - } + acpi_ut_remove_reference(info->return_object); + acpi_ex_exit_interpreter(); } cleanup: diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index c2bed56915e1..b998340e23d4 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -71,6 +71,7 @@ static unsigned int acpi_irq_irq; static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; static struct workqueue_struct *kacpid_wq; +static struct workqueue_struct *kacpi_notify_wq; static void __init acpi_request_region (struct acpi_generic_address *addr, unsigned int length, char *desc) @@ -137,8 +138,9 @@ acpi_status acpi_os_initialize1(void) return AE_NULL_ENTRY; } kacpid_wq = create_singlethread_workqueue("kacpid"); + kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify"); BUG_ON(!kacpid_wq); - + BUG_ON(!kacpi_notify_wq); return AE_OK; } @@ -150,6 +152,7 @@ acpi_status acpi_os_terminate(void) } destroy_workqueue(kacpid_wq); + destroy_workqueue(kacpi_notify_wq); return AE_OK; } @@ -603,6 +606,23 @@ void acpi_os_derive_pci_id(acpi_handle rhandle, /* upper bound */ static void acpi_os_execute_deferred(struct work_struct *work) { struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); + if (!dpc) { + printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); + return; + } + + dpc->function(dpc->context); + kfree(dpc); + + /* Yield cpu to notify thread */ + cond_resched(); + + return; +} + +static void acpi_os_execute_notify(struct work_struct *work) +{ + struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); if (!dpc) { printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); @@ -637,14 +657,12 @@ acpi_status acpi_os_execute(acpi_execute_type type, acpi_status status = AE_OK; struct acpi_os_dpc *dpc; - ACPI_FUNCTION_TRACE("os_queue_for_execution"); - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Scheduling function [%p(%p)] for deferred execution.\n", function, context)); if (!function) - return_ACPI_STATUS(AE_BAD_PARAMETER); + return AE_BAD_PARAMETER; /* * Allocate/initialize DPC structure. Note that this memory will be @@ -662,14 +680,21 @@ acpi_status acpi_os_execute(acpi_execute_type type, dpc->function = function; dpc->context = context; - INIT_WORK(&dpc->work, acpi_os_execute_deferred); - if (!queue_work(kacpid_wq, &dpc->work)) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + if (type == OSL_NOTIFY_HANDLER) { + INIT_WORK(&dpc->work, acpi_os_execute_notify); + if (!queue_work(kacpi_notify_wq, &dpc->work)) { + status = AE_ERROR; + kfree(dpc); + } + } else { + INIT_WORK(&dpc->work, acpi_os_execute_deferred); + if (!queue_work(kacpid_wq, &dpc->work)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Call to queue_work() failed.\n")); - kfree(dpc); - status = AE_ERROR; + status = AE_ERROR; + kfree(dpc); + } } - return_ACPI_STATUS(status); } diff --git a/drivers/acpi/parser/psopcode.c b/drivers/acpi/parser/psopcode.c index 16d8b6cc3c22..9296e86761d7 100644 --- a/drivers/acpi/parser/psopcode.c +++ b/drivers/acpi/parser/psopcode.c @@ -185,459 +185,453 @@ const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES] = { /* Index Name Parser Args Interpreter Args ObjectType Class Type Flags */ /* 00 */ ACPI_OP("Zero", ARGP_ZERO_OP, ARGI_ZERO_OP, ACPI_TYPE_INTEGER, - AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), + AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), /* 01 */ ACPI_OP("One", ARGP_ONE_OP, ARGI_ONE_OP, ACPI_TYPE_INTEGER, - AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), + AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), /* 02 */ ACPI_OP("Alias", ARGP_ALIAS_OP, ARGI_ALIAS_OP, - ACPI_TYPE_LOCAL_ALIAS, AML_CLASS_NAMED_OBJECT, - AML_TYPE_NAMED_SIMPLE, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE | AML_NAMED), + ACPI_TYPE_LOCAL_ALIAS, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_SIMPLE, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), /* 03 */ ACPI_OP("Name", ARGP_NAME_OP, ARGI_NAME_OP, ACPI_TYPE_ANY, - AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE | AML_NAMED), + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), /* 04 */ ACPI_OP("ByteConst", ARGP_BYTE_OP, ARGI_BYTE_OP, - ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, - AML_TYPE_LITERAL, AML_CONSTANT), + ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_CONSTANT), /* 05 */ ACPI_OP("WordConst", ARGP_WORD_OP, ARGI_WORD_OP, - ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, - AML_TYPE_LITERAL, AML_CONSTANT), + ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_CONSTANT), /* 06 */ ACPI_OP("DwordConst", ARGP_DWORD_OP, ARGI_DWORD_OP, - ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, - AML_TYPE_LITERAL, AML_CONSTANT), + ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_CONSTANT), /* 07 */ ACPI_OP("String", ARGP_STRING_OP, ARGI_STRING_OP, - ACPI_TYPE_STRING, AML_CLASS_ARGUMENT, - AML_TYPE_LITERAL, AML_CONSTANT), + ACPI_TYPE_STRING, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_CONSTANT), /* 08 */ ACPI_OP("Scope", ARGP_SCOPE_OP, ARGI_SCOPE_OP, - ACPI_TYPE_LOCAL_SCOPE, AML_CLASS_NAMED_OBJECT, - AML_TYPE_NAMED_NO_OBJ, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE | AML_NAMED), + ACPI_TYPE_LOCAL_SCOPE, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_NO_OBJ, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), /* 09 */ ACPI_OP("Buffer", ARGP_BUFFER_OP, ARGI_BUFFER_OP, - ACPI_TYPE_BUFFER, AML_CLASS_CREATE, - AML_TYPE_CREATE_OBJECT, - AML_HAS_ARGS | AML_DEFER | AML_CONSTANT), + ACPI_TYPE_BUFFER, AML_CLASS_CREATE, + AML_TYPE_CREATE_OBJECT, + AML_HAS_ARGS | AML_DEFER | AML_CONSTANT), /* 0A */ ACPI_OP("Package", ARGP_PACKAGE_OP, ARGI_PACKAGE_OP, - ACPI_TYPE_PACKAGE, AML_CLASS_CREATE, - AML_TYPE_CREATE_OBJECT, - AML_HAS_ARGS | AML_DEFER | AML_CONSTANT), + ACPI_TYPE_PACKAGE, AML_CLASS_CREATE, + AML_TYPE_CREATE_OBJECT, + AML_HAS_ARGS | AML_DEFER | AML_CONSTANT), /* 0B */ ACPI_OP("Method", ARGP_METHOD_OP, ARGI_METHOD_OP, - ACPI_TYPE_METHOD, AML_CLASS_NAMED_OBJECT, - AML_TYPE_NAMED_COMPLEX, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE | AML_NAMED | AML_DEFER), + ACPI_TYPE_METHOD, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_COMPLEX, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED | AML_DEFER), /* 0C */ ACPI_OP("Local0", ARGP_LOCAL0, ARGI_LOCAL0, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_LOCAL_VARIABLE, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), /* 0D */ ACPI_OP("Local1", ARGP_LOCAL1, ARGI_LOCAL1, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_LOCAL_VARIABLE, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), /* 0E */ ACPI_OP("Local2", ARGP_LOCAL2, ARGI_LOCAL2, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_LOCAL_VARIABLE, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), /* 0F */ ACPI_OP("Local3", ARGP_LOCAL3, ARGI_LOCAL3, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_LOCAL_VARIABLE, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), /* 10 */ ACPI_OP("Local4", ARGP_LOCAL4, ARGI_LOCAL4, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_LOCAL_VARIABLE, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), /* 11 */ ACPI_OP("Local5", ARGP_LOCAL5, ARGI_LOCAL5, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_LOCAL_VARIABLE, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), /* 12 */ ACPI_OP("Local6", ARGP_LOCAL6, ARGI_LOCAL6, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_LOCAL_VARIABLE, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), /* 13 */ ACPI_OP("Local7", ARGP_LOCAL7, ARGI_LOCAL7, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_LOCAL_VARIABLE, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), /* 14 */ ACPI_OP("Arg0", ARGP_ARG0, ARGI_ARG0, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_METHOD_ARGUMENT, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), /* 15 */ ACPI_OP("Arg1", ARGP_ARG1, ARGI_ARG1, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_METHOD_ARGUMENT, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), /* 16 */ ACPI_OP("Arg2", ARGP_ARG2, ARGI_ARG2, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_METHOD_ARGUMENT, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), /* 17 */ ACPI_OP("Arg3", ARGP_ARG3, ARGI_ARG3, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_METHOD_ARGUMENT, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), /* 18 */ ACPI_OP("Arg4", ARGP_ARG4, ARGI_ARG4, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_METHOD_ARGUMENT, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), /* 19 */ ACPI_OP("Arg5", ARGP_ARG5, ARGI_ARG5, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_METHOD_ARGUMENT, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), /* 1A */ ACPI_OP("Arg6", ARGP_ARG6, ARGI_ARG6, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_METHOD_ARGUMENT, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), /* 1B */ ACPI_OP("Store", ARGP_STORE_OP, ARGI_STORE_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, - AML_FLAGS_EXEC_1A_1T_1R), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R), /* 1C */ ACPI_OP("RefOf", ARGP_REF_OF_OP, ARGI_REF_OF_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, - AML_FLAGS_EXEC_1A_0T_1R), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R), /* 1D */ ACPI_OP("Add", ARGP_ADD_OP, ARGI_ADD_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), /* 1E */ ACPI_OP("Concatenate", ARGP_CONCAT_OP, ARGI_CONCAT_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), /* 1F */ ACPI_OP("Subtract", ARGP_SUBTRACT_OP, ARGI_SUBTRACT_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), /* 20 */ ACPI_OP("Increment", ARGP_INCREMENT_OP, ARGI_INCREMENT_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_0T_1R, - AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), /* 21 */ ACPI_OP("Decrement", ARGP_DECREMENT_OP, ARGI_DECREMENT_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_0T_1R, - AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), /* 22 */ ACPI_OP("Multiply", ARGP_MULTIPLY_OP, ARGI_MULTIPLY_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), /* 23 */ ACPI_OP("Divide", ARGP_DIVIDE_OP, ARGI_DIVIDE_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_2T_1R, - AML_FLAGS_EXEC_2A_2T_1R | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_2T_1R, + AML_FLAGS_EXEC_2A_2T_1R | AML_CONSTANT), /* 24 */ ACPI_OP("ShiftLeft", ARGP_SHIFT_LEFT_OP, ARGI_SHIFT_LEFT_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), /* 25 */ ACPI_OP("ShiftRight", ARGP_SHIFT_RIGHT_OP, ARGI_SHIFT_RIGHT_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), /* 26 */ ACPI_OP("And", ARGP_BIT_AND_OP, ARGI_BIT_AND_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), /* 27 */ ACPI_OP("NAnd", ARGP_BIT_NAND_OP, ARGI_BIT_NAND_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), /* 28 */ ACPI_OP("Or", ARGP_BIT_OR_OP, ARGI_BIT_OR_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), /* 29 */ ACPI_OP("NOr", ARGP_BIT_NOR_OP, ARGI_BIT_NOR_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), /* 2A */ ACPI_OP("XOr", ARGP_BIT_XOR_OP, ARGI_BIT_XOR_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), /* 2B */ ACPI_OP("Not", ARGP_BIT_NOT_OP, ARGI_BIT_NOT_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, - AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), /* 2C */ ACPI_OP("FindSetLeftBit", ARGP_FIND_SET_LEFT_BIT_OP, - ARGI_FIND_SET_LEFT_BIT_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, - AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), + ARGI_FIND_SET_LEFT_BIT_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), /* 2D */ ACPI_OP("FindSetRightBit", ARGP_FIND_SET_RIGHT_BIT_OP, - ARGI_FIND_SET_RIGHT_BIT_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, - AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), + ARGI_FIND_SET_RIGHT_BIT_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), /* 2E */ ACPI_OP("DerefOf", ARGP_DEREF_OF_OP, ARGI_DEREF_OF_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R), /* 2F */ ACPI_OP("Notify", ARGP_NOTIFY_OP, ARGI_NOTIFY_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_0T_0R, AML_FLAGS_EXEC_2A_0T_0R), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_0T_0R, AML_FLAGS_EXEC_2A_0T_0R), /* 30 */ ACPI_OP("SizeOf", ARGP_SIZE_OF_OP, ARGI_SIZE_OF_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_0T_1R, - AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE), /* 31 */ ACPI_OP("Index", ARGP_INDEX_OP, ARGI_INDEX_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R), /* 32 */ ACPI_OP("Match", ARGP_MATCH_OP, ARGI_MATCH_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_6A_0T_1R, - AML_FLAGS_EXEC_6A_0T_1R | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_6A_0T_1R, + AML_FLAGS_EXEC_6A_0T_1R | AML_CONSTANT), /* 33 */ ACPI_OP("CreateDWordField", ARGP_CREATE_DWORD_FIELD_OP, - ARGI_CREATE_DWORD_FIELD_OP, - ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, - AML_TYPE_CREATE_FIELD, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | - AML_DEFER | AML_CREATE), + ARGI_CREATE_DWORD_FIELD_OP, + ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, + AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_CREATE), /* 34 */ ACPI_OP("CreateWordField", ARGP_CREATE_WORD_FIELD_OP, - ARGI_CREATE_WORD_FIELD_OP, - ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, - AML_TYPE_CREATE_FIELD, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | - AML_DEFER | AML_CREATE), + ARGI_CREATE_WORD_FIELD_OP, + ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, + AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_CREATE), /* 35 */ ACPI_OP("CreateByteField", ARGP_CREATE_BYTE_FIELD_OP, - ARGI_CREATE_BYTE_FIELD_OP, - ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, - AML_TYPE_CREATE_FIELD, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | - AML_DEFER | AML_CREATE), + ARGI_CREATE_BYTE_FIELD_OP, + ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, + AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_CREATE), /* 36 */ ACPI_OP("CreateBitField", ARGP_CREATE_BIT_FIELD_OP, - ARGI_CREATE_BIT_FIELD_OP, - ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, - AML_TYPE_CREATE_FIELD, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | - AML_DEFER | AML_CREATE), + ARGI_CREATE_BIT_FIELD_OP, + ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, + AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_CREATE), /* 37 */ ACPI_OP("ObjectType", ARGP_TYPE_OP, ARGI_TYPE_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_0T_1R, - AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE), /* 38 */ ACPI_OP("LAnd", ARGP_LAND_OP, ARGI_LAND_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, - AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC | - AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC | AML_CONSTANT), /* 39 */ ACPI_OP("LOr", ARGP_LOR_OP, ARGI_LOR_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, - AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC | - AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC | AML_CONSTANT), /* 3A */ ACPI_OP("LNot", ARGP_LNOT_OP, ARGI_LNOT_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, - AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), /* 3B */ ACPI_OP("LEqual", ARGP_LEQUAL_OP, ARGI_LEQUAL_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_0T_1R, - AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), /* 3C */ ACPI_OP("LGreater", ARGP_LGREATER_OP, ARGI_LGREATER_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_0T_1R, - AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), /* 3D */ ACPI_OP("LLess", ARGP_LLESS_OP, ARGI_LLESS_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, - AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), /* 3E */ ACPI_OP("If", ARGP_IF_OP, ARGI_IF_OP, ACPI_TYPE_ANY, - AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), + AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), /* 3F */ ACPI_OP("Else", ARGP_ELSE_OP, ARGI_ELSE_OP, ACPI_TYPE_ANY, - AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), + AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), /* 40 */ ACPI_OP("While", ARGP_WHILE_OP, ARGI_WHILE_OP, ACPI_TYPE_ANY, - AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), + AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), /* 41 */ ACPI_OP("Noop", ARGP_NOOP_OP, ARGI_NOOP_OP, ACPI_TYPE_ANY, - AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), + AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), /* 42 */ ACPI_OP("Return", ARGP_RETURN_OP, ARGI_RETURN_OP, - ACPI_TYPE_ANY, AML_CLASS_CONTROL, - AML_TYPE_CONTROL, AML_HAS_ARGS), + ACPI_TYPE_ANY, AML_CLASS_CONTROL, + AML_TYPE_CONTROL, AML_HAS_ARGS), /* 43 */ ACPI_OP("Break", ARGP_BREAK_OP, ARGI_BREAK_OP, ACPI_TYPE_ANY, - AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), + AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), /* 44 */ ACPI_OP("BreakPoint", ARGP_BREAK_POINT_OP, ARGI_BREAK_POINT_OP, - ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), + ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), /* 45 */ ACPI_OP("Ones", ARGP_ONES_OP, ARGI_ONES_OP, ACPI_TYPE_INTEGER, - AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), + AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), /* Prefixed opcodes (Two-byte opcodes with a prefix op) */ /* 46 */ ACPI_OP("Mutex", ARGP_MUTEX_OP, ARGI_MUTEX_OP, ACPI_TYPE_MUTEX, - AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE | AML_NAMED), + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), /* 47 */ ACPI_OP("Event", ARGP_EVENT_OP, ARGI_EVENT_OP, ACPI_TYPE_EVENT, - AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, - AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, + AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), /* 48 */ ACPI_OP("CondRefOf", ARGP_COND_REF_OF_OP, ARGI_COND_REF_OF_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R), /* 49 */ ACPI_OP("CreateField", ARGP_CREATE_FIELD_OP, - ARGI_CREATE_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, - AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | - AML_DEFER | AML_FIELD | AML_CREATE), + ARGI_CREATE_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, + AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_FIELD | AML_CREATE), /* 4A */ ACPI_OP("Load", ARGP_LOAD_OP, ARGI_LOAD_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_0R, - AML_FLAGS_EXEC_1A_1T_0R), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_0R, + AML_FLAGS_EXEC_1A_1T_0R), /* 4B */ ACPI_OP("Stall", ARGP_STALL_OP, ARGI_STALL_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, - AML_FLAGS_EXEC_1A_0T_0R), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, + AML_FLAGS_EXEC_1A_0T_0R), /* 4C */ ACPI_OP("Sleep", ARGP_SLEEP_OP, ARGI_SLEEP_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, - AML_FLAGS_EXEC_1A_0T_0R), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, + AML_FLAGS_EXEC_1A_0T_0R), /* 4D */ ACPI_OP("Acquire", ARGP_ACQUIRE_OP, ARGI_ACQUIRE_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R), /* 4E */ ACPI_OP("Signal", ARGP_SIGNAL_OP, ARGI_SIGNAL_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), /* 4F */ ACPI_OP("Wait", ARGP_WAIT_OP, ARGI_WAIT_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, - AML_FLAGS_EXEC_2A_0T_1R), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R), /* 50 */ ACPI_OP("Reset", ARGP_RESET_OP, ARGI_RESET_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, - AML_FLAGS_EXEC_1A_0T_0R), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, + AML_FLAGS_EXEC_1A_0T_0R), /* 51 */ ACPI_OP("Release", ARGP_RELEASE_OP, ARGI_RELEASE_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), /* 52 */ ACPI_OP("FromBCD", ARGP_FROM_BCD_OP, ARGI_FROM_BCD_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_1T_1R, - AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), /* 53 */ ACPI_OP("ToBCD", ARGP_TO_BCD_OP, ARGI_TO_BCD_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, - AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), /* 54 */ ACPI_OP("Unload", ARGP_UNLOAD_OP, ARGI_UNLOAD_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), /* 55 */ ACPI_OP("Revision", ARGP_REVISION_OP, ARGI_REVISION_OP, - ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, - AML_TYPE_CONSTANT, 0), + ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, + AML_TYPE_CONSTANT, 0), /* 56 */ ACPI_OP("Debug", ARGP_DEBUG_OP, ARGI_DEBUG_OP, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_CONSTANT, 0), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_CONSTANT, 0), /* 57 */ ACPI_OP("Fatal", ARGP_FATAL_OP, ARGI_FATAL_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_0T_0R, - AML_FLAGS_EXEC_3A_0T_0R), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_0T_0R, + AML_FLAGS_EXEC_3A_0T_0R), /* 58 */ ACPI_OP("OperationRegion", ARGP_REGION_OP, ARGI_REGION_OP, - ACPI_TYPE_REGION, AML_CLASS_NAMED_OBJECT, - AML_TYPE_NAMED_COMPLEX, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE | AML_NAMED | AML_DEFER), + ACPI_TYPE_REGION, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_COMPLEX, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED | AML_DEFER), /* 59 */ ACPI_OP("Field", ARGP_FIELD_OP, ARGI_FIELD_OP, ACPI_TYPE_ANY, - AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_FIELD), + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_FIELD), /* 5A */ ACPI_OP("Device", ARGP_DEVICE_OP, ARGI_DEVICE_OP, - ACPI_TYPE_DEVICE, AML_CLASS_NAMED_OBJECT, - AML_TYPE_NAMED_NO_OBJ, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE | AML_NAMED), + ACPI_TYPE_DEVICE, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_NO_OBJ, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), /* 5B */ ACPI_OP("Processor", ARGP_PROCESSOR_OP, ARGI_PROCESSOR_OP, - ACPI_TYPE_PROCESSOR, AML_CLASS_NAMED_OBJECT, - AML_TYPE_NAMED_SIMPLE, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE | AML_NAMED), + ACPI_TYPE_PROCESSOR, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_SIMPLE, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), /* 5C */ ACPI_OP("PowerResource", ARGP_POWER_RES_OP, ARGI_POWER_RES_OP, - ACPI_TYPE_POWER, AML_CLASS_NAMED_OBJECT, - AML_TYPE_NAMED_SIMPLE, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE | AML_NAMED), + ACPI_TYPE_POWER, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_SIMPLE, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), /* 5D */ ACPI_OP("ThermalZone", ARGP_THERMAL_ZONE_OP, - ARGI_THERMAL_ZONE_OP, ACPI_TYPE_THERMAL, - AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_NO_OBJ, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE | AML_NAMED), + ARGI_THERMAL_ZONE_OP, ACPI_TYPE_THERMAL, + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_NO_OBJ, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), /* 5E */ ACPI_OP("IndexField", ARGP_INDEX_FIELD_OP, ARGI_INDEX_FIELD_OP, - ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, - AML_TYPE_NAMED_FIELD, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_FIELD), + ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_FIELD), /* 5F */ ACPI_OP("BankField", ARGP_BANK_FIELD_OP, ARGI_BANK_FIELD_OP, - ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, - AML_TYPE_NAMED_FIELD, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_FIELD), + ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_FIELD), /* Internal opcodes that map to invalid AML opcodes */ /* 60 */ ACPI_OP("LNotEqual", ARGP_LNOTEQUAL_OP, ARGI_LNOTEQUAL_OP, - ACPI_TYPE_ANY, AML_CLASS_INTERNAL, - AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_INTERNAL, + AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), /* 61 */ ACPI_OP("LLessEqual", ARGP_LLESSEQUAL_OP, ARGI_LLESSEQUAL_OP, - ACPI_TYPE_ANY, AML_CLASS_INTERNAL, - AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_INTERNAL, + AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), /* 62 */ ACPI_OP("LGreaterEqual", ARGP_LGREATEREQUAL_OP, - ARGI_LGREATEREQUAL_OP, ACPI_TYPE_ANY, - AML_CLASS_INTERNAL, AML_TYPE_BOGUS, - AML_HAS_ARGS | AML_CONSTANT), + ARGI_LGREATEREQUAL_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, + AML_HAS_ARGS | AML_CONSTANT), /* 63 */ ACPI_OP("-NamePath-", ARGP_NAMEPATH_OP, ARGI_NAMEPATH_OP, - ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, - AML_TYPE_LITERAL, AML_NSOBJECT | AML_NSNODE), + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_NSOBJECT | AML_NSNODE), /* 64 */ ACPI_OP("-MethodCall-", ARGP_METHODCALL_OP, ARGI_METHODCALL_OP, - ACPI_TYPE_METHOD, AML_CLASS_METHOD_CALL, - AML_TYPE_METHOD_CALL, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE), + ACPI_TYPE_METHOD, AML_CLASS_METHOD_CALL, + AML_TYPE_METHOD_CALL, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE), /* 65 */ ACPI_OP("-ByteList-", ARGP_BYTELIST_OP, ARGI_BYTELIST_OP, - ACPI_TYPE_ANY, AML_CLASS_ARGUMENT, - AML_TYPE_LITERAL, 0), + ACPI_TYPE_ANY, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, 0), /* 66 */ ACPI_OP("-ReservedField-", ARGP_RESERVEDFIELD_OP, - ARGI_RESERVEDFIELD_OP, ACPI_TYPE_ANY, - AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), + ARGI_RESERVEDFIELD_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), /* 67 */ ACPI_OP("-NamedField-", ARGP_NAMEDFIELD_OP, ARGI_NAMEDFIELD_OP, - ACPI_TYPE_ANY, AML_CLASS_INTERNAL, - AML_TYPE_BOGUS, - AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), + ACPI_TYPE_ANY, AML_CLASS_INTERNAL, + AML_TYPE_BOGUS, + AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), /* 68 */ ACPI_OP("-AccessField-", ARGP_ACCESSFIELD_OP, - ARGI_ACCESSFIELD_OP, ACPI_TYPE_ANY, - AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), + ARGI_ACCESSFIELD_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), /* 69 */ ACPI_OP("-StaticString", ARGP_STATICSTRING_OP, - ARGI_STATICSTRING_OP, ACPI_TYPE_ANY, - AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), + ARGI_STATICSTRING_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), /* 6A */ ACPI_OP("-Return Value-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, - AML_CLASS_RETURN_VALUE, AML_TYPE_RETURN, - AML_HAS_ARGS | AML_HAS_RETVAL), + AML_CLASS_RETURN_VALUE, AML_TYPE_RETURN, + AML_HAS_ARGS | AML_HAS_RETVAL), /* 6B */ ACPI_OP("-UNKNOWN_OP-", ARG_NONE, ARG_NONE, ACPI_TYPE_INVALID, - AML_CLASS_UNKNOWN, AML_TYPE_BOGUS, AML_HAS_ARGS), + AML_CLASS_UNKNOWN, AML_TYPE_BOGUS, AML_HAS_ARGS), /* 6C */ ACPI_OP("-ASCII_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, - AML_CLASS_ASCII, AML_TYPE_BOGUS, AML_HAS_ARGS), + AML_CLASS_ASCII, AML_TYPE_BOGUS, AML_HAS_ARGS), /* 6D */ ACPI_OP("-PREFIX_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, - AML_CLASS_PREFIX, AML_TYPE_BOGUS, AML_HAS_ARGS), + AML_CLASS_PREFIX, AML_TYPE_BOGUS, AML_HAS_ARGS), /* ACPI 2.0 opcodes */ /* 6E */ ACPI_OP("QwordConst", ARGP_QWORD_OP, ARGI_QWORD_OP, - ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, - AML_TYPE_LITERAL, AML_CONSTANT), + ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_CONSTANT), /* 6F */ ACPI_OP("Package", /* Var */ ARGP_VAR_PACKAGE_OP, ARGI_VAR_PACKAGE_OP, ACPI_TYPE_PACKAGE, AML_CLASS_CREATE, AML_TYPE_CREATE_OBJECT, AML_HAS_ARGS | AML_DEFER), /* 70 */ ACPI_OP("ConcatenateResTemplate", ARGP_CONCAT_RES_OP, - ARGI_CONCAT_RES_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), + ARGI_CONCAT_RES_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), /* 71 */ ACPI_OP("Mod", ARGP_MOD_OP, ARGI_MOD_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), /* 72 */ ACPI_OP("CreateQWordField", ARGP_CREATE_QWORD_FIELD_OP, - ARGI_CREATE_QWORD_FIELD_OP, - ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, - AML_TYPE_CREATE_FIELD, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | - AML_DEFER | AML_CREATE), + ARGI_CREATE_QWORD_FIELD_OP, + ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, + AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_CREATE), /* 73 */ ACPI_OP("ToBuffer", ARGP_TO_BUFFER_OP, ARGI_TO_BUFFER_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_1T_1R, - AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), /* 74 */ ACPI_OP("ToDecimalString", ARGP_TO_DEC_STR_OP, - ARGI_TO_DEC_STR_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, - AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), + ARGI_TO_DEC_STR_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), /* 75 */ ACPI_OP("ToHexString", ARGP_TO_HEX_STR_OP, ARGI_TO_HEX_STR_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_1T_1R, - AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), /* 76 */ ACPI_OP("ToInteger", ARGP_TO_INTEGER_OP, ARGI_TO_INTEGER_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_1T_1R, - AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), /* 77 */ ACPI_OP("ToString", ARGP_TO_STRING_OP, ARGI_TO_STRING_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_2A_1T_1R, - AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), /* 78 */ ACPI_OP("CopyObject", ARGP_COPY_OP, ARGI_COPY_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R), /* 79 */ ACPI_OP("Mid", ARGP_MID_OP, ARGI_MID_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_1T_1R, - AML_FLAGS_EXEC_3A_1T_1R | AML_CONSTANT), + AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_1T_1R, + AML_FLAGS_EXEC_3A_1T_1R | AML_CONSTANT), /* 7A */ ACPI_OP("Continue", ARGP_CONTINUE_OP, ARGI_CONTINUE_OP, - ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), + ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), /* 7B */ ACPI_OP("LoadTable", ARGP_LOAD_TABLE_OP, ARGI_LOAD_TABLE_OP, - ACPI_TYPE_ANY, AML_CLASS_EXECUTE, - AML_TYPE_EXEC_6A_0T_1R, AML_FLAGS_EXEC_6A_0T_1R), + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_6A_0T_1R, AML_FLAGS_EXEC_6A_0T_1R), /* 7C */ ACPI_OP("DataTableRegion", ARGP_DATA_REGION_OP, - ARGI_DATA_REGION_OP, ACPI_TYPE_REGION, - AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE | AML_NAMED), + ARGI_DATA_REGION_OP, ACPI_TYPE_REGION, + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), /* 7D */ ACPI_OP("[EvalSubTree]", ARGP_SCOPE_OP, ARGI_SCOPE_OP, - ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, - AML_TYPE_NAMED_NO_OBJ, - AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | - AML_NSNODE), + ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_NO_OBJ, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE), /* ACPI 3.0 opcodes */ /* 7E */ ACPI_OP("Timer", ARGP_TIMER_OP, ARGI_TIMER_OP, ACPI_TYPE_ANY, - AML_CLASS_EXECUTE, AML_TYPE_EXEC_0A_0T_1R, - AML_FLAGS_EXEC_0A_0T_1R) + AML_CLASS_EXECUTE, AML_TYPE_EXEC_0A_0T_1R, + AML_FLAGS_EXEC_0A_0T_1R) /*! [End] no source code translation !*/ }; diff --git a/drivers/acpi/resources/rscalc.c b/drivers/acpi/resources/rscalc.c index 8c6d3fdec38a..0dd2ce8a3475 100644 --- a/drivers/acpi/resources/rscalc.c +++ b/drivers/acpi/resources/rscalc.c @@ -567,7 +567,8 @@ acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object, (*sub_object_list)->string. length + 1); } else { - temp_size_needed += acpi_ns_get_pathname_length((*sub_object_list)->reference.node); + temp_size_needed += + acpi_ns_get_pathname_length((*sub_object_list)->reference.node); } } else { /* diff --git a/drivers/acpi/resources/rscreate.c b/drivers/acpi/resources/rscreate.c index cc48ab05676c..50da494c3ee2 100644 --- a/drivers/acpi/resources/rscreate.c +++ b/drivers/acpi/resources/rscreate.c @@ -267,16 +267,19 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, * If BIOS erroneously reversed the _PRT source_name and source_index, * then reverse them back. */ - if (ACPI_GET_OBJECT_TYPE (sub_object_list[3]) != ACPI_TYPE_INTEGER) { + if (ACPI_GET_OBJECT_TYPE(sub_object_list[3]) != + ACPI_TYPE_INTEGER) { if (acpi_gbl_enable_interpreter_slack) { source_name_index = 3; source_index_index = 2; - printk(KERN_WARNING "ACPI: Handling Garbled _PRT entry\n"); + printk(KERN_WARNING + "ACPI: Handling Garbled _PRT entry\n"); } else { ACPI_ERROR((AE_INFO, - "(PRT[%X].source_index) Need Integer, found %s", - index, - acpi_ut_get_object_type_name(sub_object_list[3]))); + "(PRT[%X].source_index) Need Integer, found %s", + index, + acpi_ut_get_object_type_name + (sub_object_list[3]))); return_ACPI_STATUS(AE_BAD_DATA); } } diff --git a/drivers/acpi/resources/rsdump.c b/drivers/acpi/resources/rsdump.c index de20a5d6decf..46da116a4030 100644 --- a/drivers/acpi/resources/rsdump.c +++ b/drivers/acpi/resources/rsdump.c @@ -46,7 +46,6 @@ #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsdump") - #if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) /* Local prototypes */ static void acpi_rs_out_string(char *title, char *value); @@ -489,10 +488,9 @@ acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table) /* * Optional resource_source for Address resources */ - acpi_rs_dump_resource_source(ACPI_CAST_PTR - (struct - acpi_resource_source, - target)); + acpi_rs_dump_resource_source(ACPI_CAST_PTR(struct + acpi_resource_source, + target)); break; default: diff --git a/drivers/acpi/resources/rsinfo.c b/drivers/acpi/resources/rsinfo.c index 7e3c335ab320..2c2adb6292c1 100644 --- a/drivers/acpi/resources/rsinfo.c +++ b/drivers/acpi/resources/rsinfo.c @@ -142,7 +142,7 @@ struct acpi_rsdump_info *acpi_gbl_dump_resource_dispatch[] = { }; #endif -#endif /* ACPI_FUTURE_USAGE */ +#endif /* ACPI_FUTURE_USAGE */ /* * Base sizes for external AML resource descriptors, indexed by internal type. * Includes size of the descriptor header (1 byte for small descriptors, diff --git a/drivers/acpi/resources/rslist.c b/drivers/acpi/resources/rslist.c index a92755c8877d..ca21e4660c79 100644 --- a/drivers/acpi/resources/rslist.c +++ b/drivers/acpi/resources/rslist.c @@ -153,10 +153,9 @@ acpi_rs_convert_resources_to_aml(struct acpi_resource *resource, /* Perform the conversion */ - status = acpi_rs_convert_resource_to_aml(resource, - ACPI_CAST_PTR(union - aml_resource, - aml), + status = acpi_rs_convert_resource_to_aml(resource, ACPI_CAST_PTR(union + aml_resource, + aml), acpi_gbl_set_resource_dispatch [resource->type]); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/resources/rsmisc.c b/drivers/acpi/resources/rsmisc.c index 3b63b561b94e..c7081afa893a 100644 --- a/drivers/acpi/resources/rsmisc.c +++ b/drivers/acpi/resources/rsmisc.c @@ -46,7 +46,6 @@ #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsmisc") - #define INIT_RESOURCE_TYPE(i) i->resource_offset #define INIT_RESOURCE_LENGTH(i) i->aml_offset #define INIT_TABLE_LENGTH(i) i->value @@ -429,8 +428,7 @@ acpi_rs_convert_resource_to_aml(struct acpi_resource *resource, * Optional resource_source (Index and String) */ aml_length = - acpi_rs_set_resource_source(aml, - (acpi_rs_length) + acpi_rs_set_resource_source(aml, (acpi_rs_length) aml_length, source); acpi_rs_set_resource_length(aml_length, aml); break; diff --git a/drivers/acpi/resources/rsutils.c b/drivers/acpi/resources/rsutils.c index 2442a8f8df57..11c0bd7b9cfd 100644 --- a/drivers/acpi/resources/rsutils.c +++ b/drivers/acpi/resources/rsutils.c @@ -353,10 +353,8 @@ acpi_rs_get_resource_source(acpi_rs_length resource_length, * * Zero the entire area of the buffer. */ - total_length = - (u32) - ACPI_STRLEN(ACPI_CAST_PTR(char, &aml_resource_source[1])) + - 1; + total_length = (u32) + ACPI_STRLEN(ACPI_CAST_PTR(char, &aml_resource_source[1])) + 1; total_length = (u32) ACPI_ROUND_UP_TO_NATIVE_WORD(total_length); ACPI_MEMSET(resource_source->string_ptr, 0, total_length); diff --git a/drivers/acpi/resources/rsxface.c b/drivers/acpi/resources/rsxface.c index 991f8901498c..f63813a358c5 100644 --- a/drivers/acpi/resources/rsxface.c +++ b/drivers/acpi/resources/rsxface.c @@ -217,7 +217,6 @@ acpi_get_current_resources(acpi_handle device_handle, } ACPI_EXPORT_SYMBOL(acpi_get_current_resources) - #ifdef ACPI_FUTURE_USAGE /******************************************************************************* * @@ -261,7 +260,6 @@ acpi_get_possible_resources(acpi_handle device_handle, ACPI_EXPORT_SYMBOL(acpi_get_possible_resources) #endif /* ACPI_FUTURE_USAGE */ - /******************************************************************************* * * FUNCTION: acpi_set_current_resources @@ -496,7 +494,6 @@ ACPI_EXPORT_SYMBOL(acpi_rs_match_vendor_resource) * each resource in the list. * ******************************************************************************/ - acpi_status acpi_walk_resources(acpi_handle device_handle, char *name, diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 52b23471dd69..bc7e16ec8393 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -222,7 +222,7 @@ static struct hibernation_ops acpi_hibernation_ops = { .enter = acpi_hibernation_enter, .finish = acpi_hibernation_finish, }; -#endif /* CONFIG_SOFTWARE_SUSPEND */ +#endif /* CONFIG_SOFTWARE_SUSPEND */ /* * Toshiba fails to preserve interrupts over S1, reinitialization @@ -276,4 +276,3 @@ int __init acpi_sleep_init(void) return 0; } - diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 76b45f0b8341..61f1822cc350 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -349,8 +349,7 @@ acpi_system_write_alarm(struct file *file, end: return_VALUE(result ? result : count); } -#endif /* HAVE_ACPI_LEGACY_ALARM */ - +#endif /* HAVE_ACPI_LEGACY_ALARM */ extern struct list_head acpi_wakeup_device_list; extern spinlock_t acpi_device_lock; @@ -380,8 +379,8 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) dev->wakeup.state.enabled ? "enabled" : "disabled"); if (ldev) seq_printf(seq, "%s:%s", - ldev->bus ? ldev->bus->name : "no-bus", - ldev->bus_id); + ldev->bus ? ldev->bus->name : "no-bus", + ldev->bus_id); seq_printf(seq, "\n"); put_device(ldev); @@ -490,7 +489,7 @@ static u32 rtc_handler(void *context) return ACPI_INTERRUPT_HANDLED; } -#endif /* HAVE_ACPI_LEGACY_ALARM */ +#endif /* HAVE_ACPI_LEGACY_ALARM */ static int __init acpi_sleep_proc_init(void) { @@ -517,7 +516,7 @@ static int __init acpi_sleep_proc_init(void) entry->proc_fops = &acpi_system_alarm_fops; acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL); -#endif /* HAVE_ACPI_LEGACY_ALARM */ +#endif /* HAVE_ACPI_LEGACY_ALARM */ /* 'wakeup device' [R/W] */ entry = diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/tables/tbfadt.c index 1db833eb2417..1285e91474fb 100644 --- a/drivers/acpi/tables/tbfadt.c +++ b/drivers/acpi/tables/tbfadt.c @@ -334,7 +334,8 @@ static void acpi_tb_convert_fadt(void) (acpi_gbl_FADT.xpm1a_event_block.address + pm1_register_length)); /* Don't forget to copy space_id of the GAS */ - acpi_gbl_xpm1a_enable.space_id = acpi_gbl_FADT.xpm1a_event_block.space_id; + acpi_gbl_xpm1a_enable.space_id = + acpi_gbl_FADT.xpm1a_event_block.space_id; /* The PM1B register block is optional, ignore if not present */ @@ -344,7 +345,8 @@ static void acpi_tb_convert_fadt(void) (acpi_gbl_FADT.xpm1b_event_block. address + pm1_register_length)); /* Don't forget to copy space_id of the GAS */ - acpi_gbl_xpm1b_enable.space_id = acpi_gbl_FADT.xpm1a_event_block.space_id; + acpi_gbl_xpm1b_enable.space_id = + acpi_gbl_FADT.xpm1a_event_block.space_id; } diff --git a/drivers/acpi/tables/tbxface.c b/drivers/acpi/tables/tbxface.c index 417ef5fa7666..5b302c4e293f 100644 --- a/drivers/acpi/tables/tbxface.c +++ b/drivers/acpi/tables/tbxface.c @@ -201,6 +201,7 @@ acpi_status acpi_reallocate_root_table(void) return_ACPI_STATUS(AE_OK); } + /******************************************************************************* * * FUNCTION: acpi_load_table @@ -262,7 +263,7 @@ ACPI_EXPORT_SYMBOL(acpi_load_table) acpi_status acpi_get_table_header(char *signature, acpi_native_uint instance, - struct acpi_table_header *out_table_header) + struct acpi_table_header * out_table_header) { acpi_native_uint i; acpi_native_uint j; @@ -321,7 +322,6 @@ acpi_get_table_header(char *signature, ACPI_EXPORT_SYMBOL(acpi_get_table_header) - /****************************************************************************** * * FUNCTION: acpi_unload_table_id @@ -346,11 +346,11 @@ acpi_status acpi_unload_table_id(acpi_owner_id id) continue; } /* - * Delete all namespace objects owned by this table. Note that these - * objects can appear anywhere in the namespace by virtue of the AML - * "Scope" operator. Thus, we need to track ownership by an ID, not - * simply a position within the hierarchy - */ + * Delete all namespace objects owned by this table. Note that these + * objects can appear anywhere in the namespace by virtue of the AML + * "Scope" operator. Thus, we need to track ownership by an ID, not + * simply a position within the hierarchy + */ acpi_tb_delete_namespace_by_owner(i); status = acpi_tb_release_owner_id(i); acpi_tb_set_table_loaded_flag(i, FALSE); @@ -376,7 +376,7 @@ ACPI_EXPORT_SYMBOL(acpi_unload_table_id) *****************************************************************************/ acpi_status acpi_get_table(char *signature, - acpi_native_uint instance, struct acpi_table_header ** out_table) + acpi_native_uint instance, struct acpi_table_header **out_table) { acpi_native_uint i; acpi_native_uint j; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 589b98b7b216..1ada017d01ef 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -59,8 +59,6 @@ #define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0 #define ACPI_THERMAL_NOTIFY_HOT 0xF1 #define ACPI_THERMAL_MODE_ACTIVE 0x00 -#define ACPI_THERMAL_MODE_PASSIVE 0x01 -#define ACPI_THERMAL_MODE_CRITICAL 0xff #define ACPI_THERMAL_PATH_POWEROFF "/sbin/poweroff" #define ACPI_THERMAL_MAX_ACTIVE 10 @@ -86,9 +84,6 @@ static int acpi_thermal_resume(struct acpi_device *device); static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file); static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file); static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file); -static ssize_t acpi_thermal_write_trip_points(struct file *, - const char __user *, size_t, - loff_t *); static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file); static ssize_t acpi_thermal_write_cooling_mode(struct file *, const char __user *, size_t, @@ -167,7 +162,6 @@ struct acpi_thermal { unsigned long temperature; unsigned long last_temperature; unsigned long polling_frequency; - u8 cooling_mode; volatile u8 zombie; struct acpi_thermal_flags flags; struct acpi_thermal_state state; @@ -193,7 +187,6 @@ static const struct file_operations acpi_thermal_temp_fops = { static const struct file_operations acpi_thermal_trip_fops = { .open = acpi_thermal_trip_open_fs, .read = seq_read, - .write = acpi_thermal_write_trip_points, .llseek = seq_lseek, .release = single_release, }; @@ -297,11 +290,6 @@ static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode) if (ACPI_FAILURE(status)) return -ENODEV; - tz->cooling_mode = mode; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling mode [%s]\n", - mode ? "passive" : "active")); - return 0; } @@ -889,67 +877,6 @@ static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file) return single_open(file, acpi_thermal_trip_seq_show, PDE(inode)->data); } -static ssize_t -acpi_thermal_write_trip_points(struct file *file, - const char __user * buffer, - size_t count, loff_t * ppos) -{ - struct seq_file *m = file->private_data; - struct acpi_thermal *tz = m->private; - - char *limit_string; - int num, critical, hot, passive; - int *active; - int i = 0; - - - limit_string = kzalloc(ACPI_THERMAL_MAX_LIMIT_STR_LEN, GFP_KERNEL); - if (!limit_string) - return -ENOMEM; - - active = kmalloc(ACPI_THERMAL_MAX_ACTIVE * sizeof(int), GFP_KERNEL); - if (!active) { - kfree(limit_string); - return -ENOMEM; - } - - if (!tz || (count > ACPI_THERMAL_MAX_LIMIT_STR_LEN - 1)) { - count = -EINVAL; - goto end; - } - - if (copy_from_user(limit_string, buffer, count)) { - count = -EFAULT; - goto end; - } - - limit_string[count] = '\0'; - - num = sscanf(limit_string, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", - &critical, &hot, &passive, - &active[0], &active[1], &active[2], &active[3], &active[4], - &active[5], &active[6], &active[7], &active[8], - &active[9]); - if (!(num >= 5 && num < (ACPI_THERMAL_MAX_ACTIVE + 3))) { - count = -EINVAL; - goto end; - } - - tz->trips.critical.temperature = CELSIUS_TO_KELVIN(critical); - tz->trips.hot.temperature = CELSIUS_TO_KELVIN(hot); - tz->trips.passive.temperature = CELSIUS_TO_KELVIN(passive); - for (i = 0; i < num - 3; i++) { - if (!(tz->trips.active[i].flags.valid)) - break; - tz->trips.active[i].temperature = CELSIUS_TO_KELVIN(active[i]); - } - - end: - kfree(active); - kfree(limit_string); - return count; -} - static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset) { struct acpi_thermal *tz = seq->private; @@ -958,15 +885,10 @@ static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset) if (!tz) goto end; - if (!tz->flags.cooling_mode) { + if (!tz->flags.cooling_mode) seq_puts(seq, "<setting not supported>\n"); - } - - if (tz->cooling_mode == ACPI_THERMAL_MODE_CRITICAL) - seq_printf(seq, "cooling mode: critical\n"); else - seq_printf(seq, "cooling mode: %s\n", - tz->cooling_mode ? "passive" : "active"); + seq_puts(seq, "0 - Active; 1 - Passive\n"); end: return 0; @@ -1223,28 +1145,6 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz) result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE); if (!result) tz->flags.cooling_mode = 1; - else { - /* Oh,we have not _SCP method. - Generally show cooling_mode by _ACx, _PSV,spec 12.2 */ - tz->flags.cooling_mode = 0; - if (tz->trips.active[0].flags.valid - && tz->trips.passive.flags.valid) { - if (tz->trips.passive.temperature > - tz->trips.active[0].temperature) - tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE; - else - tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE; - } else if (!tz->trips.active[0].flags.valid - && tz->trips.passive.flags.valid) { - tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE; - } else if (tz->trips.active[0].flags.valid - && !tz->trips.passive.flags.valid) { - tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE; - } else { - /* _ACx and _PSV are optional, but _CRT is required */ - tz->cooling_mode = ACPI_THERMAL_MODE_CRITICAL; - } - } /* Get default polling frequency [_TZP] (optional) */ if (tzp) diff --git a/drivers/acpi/utilities/utalloc.c b/drivers/acpi/utilities/utalloc.c index 55a764807499..6e56d5f7c43a 100644 --- a/drivers/acpi/utilities/utalloc.c +++ b/drivers/acpi/utilities/utalloc.c @@ -107,7 +107,6 @@ acpi_status acpi_ut_create_caches(void) if (ACPI_FAILURE(status)) { return (status); } - #ifdef ACPI_DBG_TRACK_ALLOCATIONS /* Memory allocation lists */ diff --git a/drivers/acpi/utilities/utcache.c b/drivers/acpi/utilities/utcache.c index 870f6edeb5f2..285a0f531760 100644 --- a/drivers/acpi/utilities/utcache.c +++ b/drivers/acpi/utilities/utcache.c @@ -45,7 +45,6 @@ #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utcache") - #ifdef ACPI_USE_LOCAL_CACHE /******************************************************************************* * @@ -64,7 +63,7 @@ ACPI_MODULE_NAME("utcache") acpi_status acpi_os_create_cache(char *cache_name, u16 object_size, - u16 max_depth, struct acpi_memory_list **return_cache) + u16 max_depth, struct acpi_memory_list ** return_cache) { struct acpi_memory_list *cache; diff --git a/drivers/acpi/utilities/utcopy.c b/drivers/acpi/utilities/utcopy.c index 84d529db0a66..4c1e00874dff 100644 --- a/drivers/acpi/utilities/utcopy.c +++ b/drivers/acpi/utilities/utcopy.c @@ -814,7 +814,9 @@ acpi_ut_copy_ielement_to_ielement(u8 object_type, /* * Create the object array */ - target_object->package.elements = ACPI_ALLOCATE_ZEROED(((acpi_size) source_object->package.count + 1) * sizeof(void *)); + target_object->package.elements = + ACPI_ALLOCATE_ZEROED(((acpi_size) source_object->package. + count + 1) * sizeof(void *)); if (!target_object->package.elements) { status = AE_NO_MEMORY; goto error_exit; diff --git a/drivers/acpi/utilities/utdebug.c b/drivers/acpi/utilities/utdebug.c index 61ad4f2daee2..c7e128e5369b 100644 --- a/drivers/acpi/utilities/utdebug.c +++ b/drivers/acpi/utilities/utdebug.c @@ -45,7 +45,6 @@ #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utdebug") - #ifdef ACPI_DEBUG_OUTPUT static acpi_thread_id acpi_gbl_prev_thread_id; static char *acpi_gbl_fn_entry_str = "----Entry"; @@ -181,7 +180,8 @@ acpi_ut_debug_print(u32 requested_debug_level, 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); + (unsigned long)acpi_gbl_prev_thread_id, + (unsigned long)thread_id); } acpi_gbl_prev_thread_id = thread_id; diff --git a/drivers/acpi/utilities/utdelete.c b/drivers/acpi/utilities/utdelete.c index 673a0caa4073..f777cebdc46d 100644 --- a/drivers/acpi/utilities/utdelete.c +++ b/drivers/acpi/utilities/utdelete.c @@ -170,6 +170,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) acpi_os_delete_mutex(object->mutex.os_mutex); acpi_gbl_global_lock_mutex = NULL; } else { + acpi_ex_unlink_mutex(object); acpi_os_delete_mutex(object->mutex.os_mutex); } break; diff --git a/drivers/acpi/utilities/utglobal.c b/drivers/acpi/utilities/utglobal.c index af33358a964b..1621655d6e2b 100644 --- a/drivers/acpi/utilities/utglobal.c +++ b/drivers/acpi/utilities/utglobal.c @@ -55,12 +55,10 @@ ACPI_EXPORT_SYMBOL(acpi_gbl_FADT) * Static global variable initialization. * ******************************************************************************/ - /* * We want the debug switches statically initialized so they * are already set when the debugger is entered. */ - /* Debug switch - level and trace mask */ u32 acpi_dbg_level = ACPI_DEBUG_DEFAULT; @@ -735,5 +733,5 @@ void acpi_ut_init_globals(void) } ACPI_EXPORT_SYMBOL(acpi_dbg_level) -ACPI_EXPORT_SYMBOL(acpi_dbg_layer) -ACPI_EXPORT_SYMBOL(acpi_gpe_count) + ACPI_EXPORT_SYMBOL(acpi_dbg_layer) + ACPI_EXPORT_SYMBOL(acpi_gpe_count) diff --git a/drivers/acpi/utilities/utmisc.c b/drivers/acpi/utilities/utmisc.c index 50133fffe420..2d19f71e9cfa 100644 --- a/drivers/acpi/utilities/utmisc.c +++ b/drivers/acpi/utilities/utmisc.c @@ -802,9 +802,8 @@ acpi_ut_strtoul64(char *string, u32 base, acpi_integer * ret_integer) valid_digits++; - if (sign_of0x - && ((valid_digits > 16) - || ((valid_digits > 8) && mode32))) { + if (sign_of0x && ((valid_digits > 16) + || ((valid_digits > 8) && mode32))) { /* * This is to_integer operation case. * No any restrictions for string-to-integer conversion, @@ -1049,6 +1048,7 @@ acpi_ut_exception(char *module_name, acpi_os_vprintf(format, args); acpi_os_printf(" [%X]\n", ACPI_CA_VERSION); } + EXPORT_SYMBOL(acpi_ut_exception); void ACPI_INTERNAL_VAR_XFACE diff --git a/drivers/acpi/utilities/utmutex.c b/drivers/acpi/utilities/utmutex.c index cbad2ef5987d..4820bc86d1f5 100644 --- a/drivers/acpi/utilities/utmutex.c +++ b/drivers/acpi/utilities/utmutex.c @@ -244,7 +244,7 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %lX attempting to acquire Mutex [%s]\n", - (unsigned long) this_thread_id, + (unsigned long)this_thread_id, acpi_ut_get_mutex_name(mutex_id))); status = acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex, @@ -252,7 +252,7 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) if (ACPI_SUCCESS(status)) { ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %lX acquired Mutex [%s]\n", - (unsigned long) this_thread_id, + (unsigned long)this_thread_id, acpi_ut_get_mutex_name(mutex_id))); acpi_gbl_mutex_info[mutex_id].use_count++; @@ -260,7 +260,7 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) } else { ACPI_EXCEPTION((AE_INFO, status, "Thread %lX could not acquire Mutex [%X]", - (unsigned long) this_thread_id, mutex_id)); + (unsigned long)this_thread_id, mutex_id)); } return (status); @@ -287,7 +287,7 @@ acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id) 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, + (unsigned long)this_thread_id, acpi_ut_get_mutex_name(mutex_id))); if (mutex_id > ACPI_MAX_MUTEX) { diff --git a/drivers/acpi/utilities/utresrc.c b/drivers/acpi/utilities/utresrc.c index e8fe1ba6cc24..cbbd3315a1e2 100644 --- a/drivers/acpi/utilities/utresrc.c +++ b/drivers/acpi/utilities/utresrc.c @@ -46,7 +46,6 @@ #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utresrc") - #if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER) /* * Strings used to decode resource descriptors. diff --git a/drivers/acpi/utilities/utxface.c b/drivers/acpi/utilities/utxface.c index de3276f4f468..e9a57806cd34 100644 --- a/drivers/acpi/utilities/utxface.c +++ b/drivers/acpi/utilities/utxface.c @@ -337,7 +337,6 @@ acpi_status acpi_terminate(void) } ACPI_EXPORT_SYMBOL(acpi_terminate) - #ifdef ACPI_FUTURE_USAGE /******************************************************************************* * @@ -470,7 +469,6 @@ acpi_install_initialization_handler(acpi_init_handler handler, u32 function) ACPI_EXPORT_SYMBOL(acpi_install_initialization_handler) #endif /* ACPI_FUTURE_USAGE */ - /***************************************************************************** * * FUNCTION: acpi_purge_cached_objects diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index c7219663f2b9..f031b8732330 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -3,6 +3,7 @@ # menu "Serial ATA (prod) and Parallel ATA (experimental) drivers" + depends on HAS_IOMEM config ATA tristate "ATA device support" diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index 0300e7f54cc4..2e18a63ead36 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -6,6 +6,7 @@ # menu "Auxiliary Display support" + depends on PARPORT config KS0108 tristate "KS0108 LCD Controller" diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 17ee97f3a99b..b4c8319138b2 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -444,8 +444,6 @@ config CDROM_PKTCDVD_WCACHE this option is dangerous unless the CD-RW media is known good, as we don't do deferred write error handling yet. -source "drivers/s390/block/Kconfig" - config ATA_OVER_ETH tristate "ATA over Ethernet support" depends on NET @@ -453,6 +451,8 @@ config ATA_OVER_ETH This driver provides Support for ATA over Ethernet block devices like the Coraid EtherDrive (R) Storage Blade. +source "drivers/s390/block/Kconfig" + endmenu endif diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 0f4203b499af..6055b9c0ac0f 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -307,7 +307,9 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (hu) { struct hci_dev *hdev = hu->hdev; - hci_uart_close(hdev); + + if (hdev) + hci_uart_close(hdev); if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { hu->proto->close(hu); @@ -473,12 +475,18 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, tty->low_latency = 1; } else return -EBUSY; + break; case HCIUARTGETPROTO: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) return hu->proto->id; return -EUNATCH; + case HCIUARTGETDEVICE: + if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) + return hu->hdev->id; + return -EUNATCH; + default: err = n_tty_ioctl(tty, file, cmd, arg); break; diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index b250e6789dee..1097ce72393f 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -28,8 +28,9 @@ #endif /* Ioctls */ -#define HCIUARTSETPROTO _IOW('U', 200, int) -#define HCIUARTGETPROTO _IOR('U', 201, int) +#define HCIUARTSETPROTO _IOW('U', 200, int) +#define HCIUARTGETPROTO _IOR('U', 201, int) +#define HCIUARTGETDEVICE _IOR('U', 202, int) /* UART protocols */ #define HCI_UART_MAX_PROTO 4 diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 2df42fdcdc91..abcafac64738 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -6,6 +6,7 @@ menu "Character devices" config VT bool "Virtual terminal" if EMBEDDED + depends on !S390 select INPUT default y if !VIOCONS ---help--- @@ -81,6 +82,7 @@ config VT_HW_CONSOLE_BINDING config SERIAL_NONSTANDARD bool "Non-standard serial port support" + depends on HAS_IOMEM ---help--- Say Y here if you have any non-standard serial boards -- boards which aren't supported using the standard "dumb" serial driver. @@ -765,7 +767,7 @@ config NVRAM config RTC tristate "Enhanced Real Time Clock Support" - depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM && !SUPERH + depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM && !SUPERH && !S390 ---help--- If you say Y here and create a character special file /dev/rtc with major number 10 and minor number 135 using mknod ("man mknod"), you @@ -813,7 +815,7 @@ config SGI_IP27_RTC config GEN_RTC tristate "Generic /dev/rtc emulation" - depends on RTC!=y && !IA64 && !ARM && !M32R && !SPARC && !FRV + depends on RTC!=y && !IA64 && !ARM && !M32R && !SPARC && !FRV && !S390 ---help--- If you say Y here and create a character special file /dev/rtc with major number 10 and minor number 135 using mknod ("man mknod"), you @@ -858,6 +860,7 @@ config COBALT_LCD config DTLK tristate "Double Talk PC internal speech card support" + depends on ISA help This driver is for the DoubleTalk PC, a speech synthesizer manufactured by RC Systems (<http://www.rcsys.com/>). It is also @@ -1043,7 +1046,7 @@ config HPET_MMAP config HANGCHECK_TIMER tristate "Hangcheck timer" - depends on X86 || IA64 || PPC64 + depends on X86 || IA64 || PPC64 || S390 help The hangcheck-timer module detects when the system has gone out to lunch past a certain margin. It can reboot the system @@ -1078,5 +1081,7 @@ config DEVPORT depends on ISA || PCI default y +source "drivers/s390/char/Kconfig" + endmenu diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index c9f0f250d78f..801abdd29066 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -268,7 +268,7 @@ static int __devinit aperture_valid(u64 aper, u32 size) printk(KERN_ERR PFX "Aperture too small (%d MB)\n", size>>20); return 0; } - if (aper + size > 0xffffffff) { + if ((u64)aper + size > 0x100000000ULL) { printk(KERN_ERR PFX "Aperture out of bounds\n"); return 0; } diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index a6dcb2918157..b894f67fdf14 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -3,6 +3,8 @@ # menu "IPMI" + depends on HAS_IOMEM + config IPMI_HANDLER tristate 'IPMI top-level message handler' help diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 6ac3ca4c723c..b3d4ccc33a47 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -1544,21 +1544,18 @@ static unsigned int normal_poll(struct tty_struct * tty, struct file * file, pol } struct tty_ldisc tty_ldisc_N_TTY = { - TTY_LDISC_MAGIC, /* magic */ - "n_tty", /* name */ - 0, /* num */ - 0, /* flags */ - n_tty_open, /* open */ - n_tty_close, /* close */ - n_tty_flush_buffer, /* flush_buffer */ - n_tty_chars_in_buffer, /* chars_in_buffer */ - read_chan, /* read */ - write_chan, /* write */ - n_tty_ioctl, /* ioctl */ - n_tty_set_termios, /* set_termios */ - normal_poll, /* poll */ - NULL, /* hangup */ - n_tty_receive_buf, /* receive_buf */ - n_tty_write_wakeup /* write_wakeup */ + .magic = TTY_LDISC_MAGIC, + .name = "n_tty", + .open = n_tty_open, + .close = n_tty_close, + .flush_buffer = n_tty_flush_buffer, + .chars_in_buffer = n_tty_chars_in_buffer, + .read = read_chan, + .write = write_chan, + .ioctl = n_tty_ioctl, + .set_termios = n_tty_set_termios, + .poll = normal_poll, + .receive_buf = n_tty_receive_buf, + .write_wakeup = n_tty_write_wakeup }; diff --git a/drivers/char/rio/riocmd.c b/drivers/char/rio/riocmd.c index 245f03195b7c..8cc60b693460 100644 --- a/drivers/char/rio/riocmd.c +++ b/drivers/char/rio/riocmd.c @@ -402,7 +402,7 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: Host number %Zd, name ``%s''\n", HostP - p->RIOHosts, HostP->Name); rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: Rup number 0x%x\n", rup); - if (Rup >= (unsigned short) MAX_RUP) { + if (Rup < (unsigned short) MAX_RUP) { rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: This is the RUP for RTA ``%s''\n", HostP->Mapping[Rup].Name); } else rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: This is the RUP for link ``%c'' of host ``%s''\n", ('A' + Rup - MAX_RUP), HostP->Name); diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 61a63da420c2..a3fd7e7ba5a9 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -1014,9 +1014,6 @@ static int rp_open(struct tty_struct *tty, struct file *filp) /* * Info->count is now 1; so it's safe to sleep now. */ - info->session = process_session(current); - info->pgrp = process_group(current); - if ((info->flags & ROCKET_INITIALIZED) == 0) { cp = &info->channel; sSetRxTrigger(cp, TRIG_1); diff --git a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h index 89b4d7b10d12..b4c53dfa7951 100644 --- a/drivers/char/rocket_int.h +++ b/drivers/char/rocket_int.h @@ -1158,8 +1158,6 @@ struct r_port { int xmit_head; int xmit_tail; int xmit_cnt; - int session; - int pgrp; int cd_status; int ignore_status_mask; int read_status_mask; diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 2a7736b5f2f7..02b49bc00028 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -1171,6 +1171,112 @@ static int ioctl(struct tty_struct *tty, struct file *file, } /* + * support for 32 bit ioctl calls on 64 bit systems + */ +#ifdef CONFIG_COMPAT +static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params) +{ + struct MGSL_PARAMS32 tmp_params; + + DBGINFO(("%s get_params32\n", info->device_name)); + tmp_params.mode = (compat_ulong_t)info->params.mode; + tmp_params.loopback = info->params.loopback; + tmp_params.flags = info->params.flags; + tmp_params.encoding = info->params.encoding; + tmp_params.clock_speed = (compat_ulong_t)info->params.clock_speed; + tmp_params.addr_filter = info->params.addr_filter; + tmp_params.crc_type = info->params.crc_type; + tmp_params.preamble_length = info->params.preamble_length; + tmp_params.preamble = info->params.preamble; + tmp_params.data_rate = (compat_ulong_t)info->params.data_rate; + tmp_params.data_bits = info->params.data_bits; + tmp_params.stop_bits = info->params.stop_bits; + tmp_params.parity = info->params.parity; + if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32))) + return -EFAULT; + return 0; +} + +static long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params) +{ + struct MGSL_PARAMS32 tmp_params; + + DBGINFO(("%s set_params32\n", info->device_name)); + if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32))) + return -EFAULT; + + spin_lock(&info->lock); + info->params.mode = tmp_params.mode; + info->params.loopback = tmp_params.loopback; + info->params.flags = tmp_params.flags; + info->params.encoding = tmp_params.encoding; + info->params.clock_speed = tmp_params.clock_speed; + info->params.addr_filter = tmp_params.addr_filter; + info->params.crc_type = tmp_params.crc_type; + info->params.preamble_length = tmp_params.preamble_length; + info->params.preamble = tmp_params.preamble; + info->params.data_rate = tmp_params.data_rate; + info->params.data_bits = tmp_params.data_bits; + info->params.stop_bits = tmp_params.stop_bits; + info->params.parity = tmp_params.parity; + spin_unlock(&info->lock); + + change_params(info); + + return 0; +} + +static long slgt_compat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct slgt_info *info = tty->driver_data; + int rc = -ENOIOCTLCMD; + + if (sanity_check(info, tty->name, "compat_ioctl")) + return -ENODEV; + DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd)); + + switch (cmd) { + + case MGSL_IOCSPARAMS32: + rc = set_params32(info, compat_ptr(arg)); + break; + + case MGSL_IOCGPARAMS32: + rc = get_params32(info, compat_ptr(arg)); + break; + + case MGSL_IOCGPARAMS: + case MGSL_IOCSPARAMS: + case MGSL_IOCGTXIDLE: + case MGSL_IOCGSTATS: + case MGSL_IOCWAITEVENT: + case MGSL_IOCGIF: + case MGSL_IOCSGPIO: + case MGSL_IOCGGPIO: + case MGSL_IOCWAITGPIO: + case TIOCGICOUNT: + rc = ioctl(tty, file, cmd, (unsigned long)(compat_ptr(arg))); + break; + + case MGSL_IOCSTXIDLE: + case MGSL_IOCTXENABLE: + case MGSL_IOCRXENABLE: + case MGSL_IOCTXABORT: + case TIOCMIWAIT: + case MGSL_IOCSIF: + rc = ioctl(tty, file, cmd, arg); + break; + } + + DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc)); + return rc; +} +#else +#define slgt_compat_ioctl NULL +#endif /* ifdef CONFIG_COMPAT */ + +/* * proc fs support */ static inline int line_info(char *buf, struct slgt_info *info) @@ -3446,6 +3552,7 @@ static const struct tty_operations ops = { .chars_in_buffer = chars_in_buffer, .flush_buffer = flush_buffer, .ioctl = ioctl, + .compat_ioctl = slgt_compat_ioctl, .throttle = throttle, .unthrottle = unthrottle, .send_xchar = send_xchar, diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 11089be0691b..dc4e1ff7f56f 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -3,6 +3,7 @@ # menu "TPM devices" + depends on HAS_IOMEM config TCG_TPM tristate "TPM Hardware Support" diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index fc662e4ce58a..fe62c2170d01 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -151,6 +151,12 @@ static int tty_open(struct inode *, struct file *); static int tty_release(struct inode *, struct file *); int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +static long tty_compat_ioctl(struct file * file, unsigned int cmd, + unsigned long arg); +#else +#define tty_compat_ioctl NULL +#endif static int tty_fasync(int fd, struct file * filp, int on); static void release_tty(struct tty_struct *tty, int idx); static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); @@ -1143,8 +1149,8 @@ static unsigned int hung_up_tty_poll(struct file * filp, poll_table * wait) return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM; } -static int hung_up_tty_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg) +static long hung_up_tty_ioctl(struct file * file, + unsigned int cmd, unsigned long arg) { return cmd == TIOCSPGRP ? -ENOTTY : -EIO; } @@ -1155,6 +1161,7 @@ static const struct file_operations tty_fops = { .write = tty_write, .poll = tty_poll, .ioctl = tty_ioctl, + .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, .fasync = tty_fasync, @@ -1167,6 +1174,7 @@ static const struct file_operations ptmx_fops = { .write = tty_write, .poll = tty_poll, .ioctl = tty_ioctl, + .compat_ioctl = tty_compat_ioctl, .open = ptmx_open, .release = tty_release, .fasync = tty_fasync, @@ -1179,6 +1187,7 @@ static const struct file_operations console_fops = { .write = redirected_tty_write, .poll = tty_poll, .ioctl = tty_ioctl, + .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, .fasync = tty_fasync, @@ -1189,7 +1198,8 @@ static const struct file_operations hung_up_tty_fops = { .read = hung_up_tty_read, .write = hung_up_tty_write, .poll = hung_up_tty_poll, - .ioctl = hung_up_tty_ioctl, + .unlocked_ioctl = hung_up_tty_ioctl, + .compat_ioctl = hung_up_tty_ioctl, .release = tty_release, }; @@ -3357,6 +3367,32 @@ int tty_ioctl(struct inode * inode, struct file * file, return retval; } +#ifdef CONFIG_COMPAT +static long tty_compat_ioctl(struct file * file, unsigned int cmd, + unsigned long arg) +{ + struct inode *inode = file->f_dentry->d_inode; + struct tty_struct *tty = file->private_data; + struct tty_ldisc *ld; + int retval = -ENOIOCTLCMD; + + if (tty_paranoia_check(tty, inode, "tty_ioctl")) + return -EINVAL; + + if (tty->driver->compat_ioctl) { + retval = (tty->driver->compat_ioctl)(tty, file, cmd, arg); + if (retval != -ENOIOCTLCMD) + return retval; + } + + ld = tty_ldisc_ref_wait(tty); + if (ld->compat_ioctl) + retval = ld->compat_ioctl(tty, file, cmd, arg); + tty_ldisc_deref(ld); + + return retval; +} +#endif /* * This implements the "Secure Attention Key" --- the idea is to @@ -3689,6 +3725,7 @@ void tty_set_operations(struct tty_driver *driver, driver->write_room = op->write_room; driver->chars_in_buffer = op->chars_in_buffer; driver->ioctl = op->ioctl; + driver->compat_ioctl = op->compat_ioctl; driver->set_termios = op->set_termios; driver->throttle = op->throttle; driver->unthrottle = op->unthrottle; diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index 60198a78974c..1cad32c62ed3 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -2,9 +2,7 @@ # Watchdog device configuration # -menu "Watchdog Cards" - -config WATCHDOG +menuconfig WATCHDOG bool "Watchdog Timer Support" ---help--- If you say Y here (and to one of the following options) and create a @@ -28,9 +26,10 @@ config WATCHDOG If unsure, say N. +if WATCHDOG + config WATCHDOG_NOWAYOUT bool "Disable watchdog shutdown on close" - depends on WATCHDOG help The default watchdog behaviour (which you get if you say N here) is to stop the timer if the process managing it closes the file @@ -43,13 +42,11 @@ config WATCHDOG_NOWAYOUT # comment "Watchdog Device Drivers" - depends on WATCHDOG # Architecture Independent config SOFT_WATCHDOG tristate "Software watchdog" - depends on WATCHDOG help A software monitoring watchdog. This will fail to reboot your system from some situations that the hardware watchdog will recover @@ -62,14 +59,14 @@ config SOFT_WATCHDOG config AT91RM9200_WATCHDOG tristate "AT91RM9200 watchdog" - depends on WATCHDOG && ARCH_AT91RM9200 + depends on ARCH_AT91RM9200 help Watchdog timer embedded into AT91RM9200 chips. This will reboot your system when the timeout is reached. config 21285_WATCHDOG tristate "DC21285 watchdog" - depends on WATCHDOG && FOOTBRIDGE + depends on FOOTBRIDGE help The Intel Footbridge chip contains a built-in watchdog circuit. Say Y here if you wish to use this. Alternatively say M to compile the @@ -83,7 +80,7 @@ config 21285_WATCHDOG config 977_WATCHDOG tristate "NetWinder WB83C977 watchdog" - depends on WATCHDOG && FOOTBRIDGE && ARCH_NETWINDER + depends on FOOTBRIDGE && ARCH_NETWINDER help Say Y here to include support for the WB977 watchdog included in NetWinder machines. Alternatively say M to compile the driver as @@ -93,7 +90,7 @@ config 977_WATCHDOG config IXP2000_WATCHDOG tristate "IXP2000 Watchdog" - depends on WATCHDOG && ARCH_IXP2000 + depends on ARCH_IXP2000 help Say Y here if to include support for the watchdog timer in the Intel IXP2000(2400, 2800, 2850) network processors. @@ -104,7 +101,7 @@ config IXP2000_WATCHDOG config IXP4XX_WATCHDOG tristate "IXP4xx Watchdog" - depends on WATCHDOG && ARCH_IXP4XX + depends on ARCH_IXP4XX help Say Y here if to include support for the watchdog timer in the Intel IXP4xx network processors. This driver can @@ -120,7 +117,7 @@ config IXP4XX_WATCHDOG config S3C2410_WATCHDOG tristate "S3C2410 Watchdog" - depends on WATCHDOG && ARCH_S3C2410 + depends on ARCH_S3C2410 help Watchdog timer block in the Samsung S3C2410 chips. This will reboot the system when the timer expires with the watchdog @@ -136,7 +133,7 @@ config S3C2410_WATCHDOG config SA1100_WATCHDOG tristate "SA1100/PXA2xx watchdog" - depends on WATCHDOG && ( ARCH_SA1100 || ARCH_PXA ) + depends on ARCH_SA1100 || ARCH_PXA help Watchdog timer embedded into SA11x0 and PXA2xx chips. This will reboot your system when timeout is reached. @@ -148,7 +145,7 @@ config SA1100_WATCHDOG config MPCORE_WATCHDOG tristate "MPcore watchdog" - depends on WATCHDOG && ARM_MPCORE_PLATFORM && LOCAL_TIMERS + depends on ARM_MPCORE_PLATFORM && LOCAL_TIMERS help Watchdog timer embedded into the MPcore system. @@ -157,7 +154,7 @@ config MPCORE_WATCHDOG config EP93XX_WATCHDOG tristate "EP93xx Watchdog" - depends on WATCHDOG && ARCH_EP93XX + depends on ARCH_EP93XX help Say Y here if to include support for the watchdog timer embedded in the Cirrus Logic EP93xx family of devices. @@ -167,14 +164,14 @@ config EP93XX_WATCHDOG config OMAP_WATCHDOG tristate "OMAP Watchdog" - depends on WATCHDOG && (ARCH_OMAP16XX || ARCH_OMAP24XX) + depends on ARCH_OMAP16XX || ARCH_OMAP24XX help Support for TI OMAP1610/OMAP1710/OMAP2420 watchdog. Say 'Y' here to enable the OMAP1610/OMAP1710 watchdog timer. config PNX4008_WATCHDOG tristate "PNX4008 Watchdog" - depends on WATCHDOG && ARCH_PNX4008 + depends on ARCH_PNX4008 help Say Y here if to include support for the watchdog timer in the PNX4008 processor. @@ -187,7 +184,7 @@ config PNX4008_WATCHDOG config ACQUIRE_WDT tristate "Acquire SBC Watchdog Timer" - depends on WATCHDOG && X86 + depends on X86 ---help--- This is the driver for the hardware watchdog on Single Board Computers produced by Acquire Inc (and others). This watchdog @@ -201,7 +198,7 @@ config ACQUIRE_WDT config ADVANTECH_WDT tristate "Advantech SBC Watchdog Timer" - depends on WATCHDOG && X86 + depends on X86 help If you are configuring a Linux kernel for the Advantech single-board computer, say `Y' here to support its built-in watchdog timer @@ -210,7 +207,7 @@ config ADVANTECH_WDT config ALIM1535_WDT tristate "ALi M1535 PMU Watchdog Timer" - depends on WATCHDOG && X86 && PCI + depends on X86 && PCI ---help--- This is the driver for the hardware watchdog on the ALi M1535 PMU. @@ -221,7 +218,7 @@ config ALIM1535_WDT config ALIM7101_WDT tristate "ALi M7101 PMU Computer Watchdog" - depends on WATCHDOG && X86 && PCI + depends on X86 && PCI help This is the driver for the hardware watchdog on the ALi M7101 PMU as used in the x86 Cobalt servers. @@ -233,7 +230,7 @@ config ALIM7101_WDT config SC520_WDT tristate "AMD Elan SC520 processor Watchdog" - depends on WATCHDOG && X86 + depends on X86 help This is the driver for the hardware watchdog built in to the AMD "Elan" SC520 microcomputer commonly used in embedded systems. @@ -246,7 +243,7 @@ config SC520_WDT config EUROTECH_WDT tristate "Eurotech CPU-1220/1410 Watchdog Timer" - depends on WATCHDOG && X86 + depends on X86 help Enable support for the watchdog timer on the Eurotech CPU-1220 and CPU-1410 cards. These are PC/104 SBCs. Spec sheets and product @@ -254,7 +251,7 @@ config EUROTECH_WDT config IB700_WDT tristate "IB700 SBC Watchdog Timer" - depends on WATCHDOG && X86 + depends on X86 ---help--- This is the driver for the hardware watchdog on the IB700 Single Board Computer produced by TMC Technology (www.tmc-uk.com). This watchdog @@ -270,7 +267,7 @@ config IB700_WDT config IBMASR tristate "IBM Automatic Server Restart" - depends on WATCHDOG && X86 + depends on X86 help This is the driver for the IBM Automatic Server Restart watchdog timer built-in into some eServer xSeries machines. @@ -280,7 +277,7 @@ config IBMASR config WAFER_WDT tristate "ICP Wafer 5823 Single Board Computer Watchdog" - depends on WATCHDOG && X86 + depends on X86 help This is a driver for the hardware watchdog on the ICP Wafer 5823 Single Board Computer (and probably other similar models). @@ -290,7 +287,7 @@ config WAFER_WDT config I6300ESB_WDT tristate "Intel 6300ESB Timer/Watchdog" - depends on WATCHDOG && X86 && PCI + depends on X86 && PCI ---help--- Hardware driver for the watchdog timer built into the Intel 6300ESB controller hub. @@ -298,31 +295,9 @@ config I6300ESB_WDT To compile this driver as a module, choose M here: the module will be called i6300esb. -config I8XX_TCO - tristate "Intel i8xx TCO Timer/Watchdog" - depends on WATCHDOG && (X86 || IA64) && PCI - default n - ---help--- - Hardware driver for the TCO timer built into the Intel 82801 - I/O Controller Hub family. The TCO (Total Cost of Ownership) - timer is a watchdog timer that will reboot the machine after - its second expiration. The expiration time can be configured - with the "heartbeat" parameter. - - On some motherboards the driver may fail to reset the chipset's - NO_REBOOT flag which prevents the watchdog from rebooting the - machine. If this is the case you will get a kernel message like - "failed to reset NO_REBOOT flag, reboot disabled by hardware". - - To compile this driver as a module, choose M here: the - module will be called i8xx_tco. - - Note: This driver will be removed in the near future. Please - use the Intel TCO Timer/Watchdog driver. - config ITCO_WDT tristate "Intel TCO Timer/Watchdog" - depends on WATCHDOG && (X86 || IA64) && PCI + depends on (X86 || IA64) && PCI ---help--- Hardware driver for the intel TCO timer based watchdog devices. These drivers are included in the Intel 82801 I/O Controller @@ -351,7 +326,7 @@ config ITCO_VENDOR_SUPPORT config SC1200_WDT tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" - depends on WATCHDOG && X86 + depends on X86 help This is a driver for National Semiconductor PC87307/PC97307 hardware watchdog cards as found on the SC1200. This watchdog is mainly used @@ -365,7 +340,7 @@ config SC1200_WDT config SCx200_WDT tristate "National Semiconductor SCx200 Watchdog" - depends on WATCHDOG && SCx200 && PCI + depends on SCx200 && PCI help Enable the built-in watchdog timer support on the National Semiconductor SCx200 processors. @@ -374,7 +349,7 @@ config SCx200_WDT config PC87413_WDT tristate "NS PC87413 watchdog" - depends on WATCHDOG && X86 + depends on X86 ---help--- This is the driver for the hardware watchdog on the PC87413 chipset This watchdog simply watches your kernel to make sure it doesn't @@ -388,7 +363,7 @@ config PC87413_WDT config 60XX_WDT tristate "SBC-60XX Watchdog Timer" - depends on WATCHDOG && X86 + depends on X86 help This driver can be used with the watchdog timer found on some single board computers, namely the 6010 PII based computer. @@ -402,7 +377,7 @@ config 60XX_WDT config SBC8360_WDT tristate "SBC8360 Watchdog Timer" - depends on WATCHDOG && X86 + depends on X86 ---help--- This is the driver for the hardware watchdog on the SBC8360 Single @@ -415,7 +390,7 @@ config SBC8360_WDT config CPU5_WDT tristate "SMA CPU5 Watchdog" - depends on WATCHDOG && X86 + depends on X86 ---help--- TBD. To compile this driver as a module, choose M here: the @@ -423,7 +398,7 @@ config CPU5_WDT config SMSC37B787_WDT tristate "Winbond SMsC37B787 Watchdog Timer" - depends on WATCHDOG && X86 + depends on X86 ---help--- This is the driver for the hardware watchdog component on the Winbond SMsC37B787 chipset as used on the NetRunner Mainboard @@ -443,7 +418,7 @@ config SMSC37B787_WDT config W83627HF_WDT tristate "W83627HF Watchdog Timer" - depends on WATCHDOG && X86 + depends on X86 ---help--- This is the driver for the hardware watchdog on the W83627HF chipset as used in Advantech PC-9578 and Tyan S2721-533 motherboards @@ -458,7 +433,7 @@ config W83627HF_WDT config W83697HF_WDT tristate "W83697HF/W83697HG Watchdog Timer" - depends on WATCHDOG && X86 + depends on X86 ---help--- This is the driver for the hardware watchdog on the W83697HF/HG chipset as used in Dedibox/VIA motherboards (and likely others). @@ -473,7 +448,7 @@ config W83697HF_WDT config W83877F_WDT tristate "W83877F (EMACS) Watchdog Timer" - depends on WATCHDOG && X86 + depends on X86 ---help--- This is the driver for the hardware watchdog on the W83877F chipset as used in EMACS PC-104 motherboards (and likely others). This @@ -488,7 +463,7 @@ config W83877F_WDT config W83977F_WDT tristate "W83977F (PCM-5335) Watchdog Timer" - depends on WATCHDOG && X86 + depends on X86 ---help--- This is the driver for the hardware watchdog on the W83977F I/O chip as used in AAEON's PCM-5335 SBC (and likely others). This @@ -501,7 +476,7 @@ config W83977F_WDT config MACHZ_WDT tristate "ZF MachZ Watchdog" - depends on WATCHDOG && X86 + depends on X86 ---help--- If you are using a ZF Micro MachZ processor, say Y here, otherwise N. This is the driver for the watchdog timer built-in on that @@ -514,7 +489,7 @@ config MACHZ_WDT config SBC_EPX_C3_WATCHDOG tristate "Winsystems SBC EPX-C3 watchdog" - depends on WATCHDOG && X86 + depends on X86 ---help--- This is the driver for the built-in watchdog timer on the EPX-C3 Single-board computer made by Winsystems, Inc. @@ -537,19 +512,19 @@ config SBC_EPX_C3_WATCHDOG config 8xx_WDT tristate "MPC8xx Watchdog Timer" - depends on WATCHDOG && 8xx + depends on 8xx config 83xx_WDT tristate "MPC83xx Watchdog Timer" - depends on WATCHDOG && PPC_83xx + depends on PPC_83xx config MV64X60_WDT tristate "MV64X60 (Marvell Discovery) Watchdog Timer" - depends on WATCHDOG && MV64X60 + depends on MV64X60 config BOOKE_WDT bool "PowerPC Book-E Watchdog Timer" - depends on WATCHDOG && (BOOKE || 4xx) + depends on BOOKE || 4xx ---help--- Please see Documentation/watchdog/watchdog-api.txt for more information. @@ -558,7 +533,7 @@ config BOOKE_WDT config WATCHDOG_RTAS tristate "RTAS watchdog" - depends on WATCHDOG && PPC_RTAS + depends on PPC_RTAS help This driver adds watchdog support for the RTAS watchdog. @@ -569,16 +544,23 @@ config WATCHDOG_RTAS config INDYDOG tristate "Indy/I2 Hardware Watchdog" - depends on WATCHDOG && SGI_IP22 + depends on SGI_IP22 help Hardware driver for the Indy's/I2's watchdog. This is a watchdog timer that will reboot the machine after a 60 second timer expired and no process has written to /dev/watchdog during that time. +config WDT_MTX1 + tristate "MTX-1 Hardware Watchdog" + depends on MIPS_MTX1 + help + Hardware driver for the MTX-1 boards. This is a watchdog timer that + will reboot the machine after a 100 seconds timer expired. + config WDT_RM9K_GPI tristate "RM9000/GPI hardware watchdog" - depends on WATCHDOG && CPU_RM9000 + depends on CPU_RM9000 help Watchdog implementation using the GPI hardware found on PMC-Sierra RM9xxx CPUs. @@ -590,7 +572,7 @@ config WDT_RM9K_GPI config ZVM_WATCHDOG tristate "z/VM Watchdog Timer" - depends on WATCHDOG && S390 + depends on S390 help IBM s/390 and zSeries machines running under z/VM 5.1 or later provide a virtual watchdog timer to their guest that cause a @@ -604,7 +586,7 @@ config ZVM_WATCHDOG config SH_WDT tristate "SuperH Watchdog" - depends on WATCHDOG && SUPERH + depends on SUPERH help This driver adds watchdog support for the integrated watchdog in the SuperH processors. If you have one of these processors and wish @@ -631,7 +613,7 @@ config SH_WDT_MMAP config WATCHDOG_CP1XXX tristate "CP1XXX Hardware Watchdog support" - depends on WATCHDOG && SPARC64 && PCI + depends on SPARC64 && PCI ---help--- This is the driver for the hardware watchdog timers present on Sun Microsystems CompactPCI models CP1400 and CP1500. @@ -645,7 +627,7 @@ config WATCHDOG_CP1XXX config WATCHDOG_RIO tristate "RIO Hardware Watchdog support" - depends on WATCHDOG && SPARC64 && PCI + depends on SPARC64 && PCI help Say Y here to support the hardware watchdog capability on Sun RIO machines. The watchdog timeout period is normally one minute but @@ -656,11 +638,11 @@ config WATCHDOG_RIO # comment "ISA-based Watchdog Cards" - depends on WATCHDOG && ISA + depends on ISA config PCWATCHDOG tristate "Berkshire Products ISA-PC Watchdog" - depends on WATCHDOG && ISA + depends on ISA ---help--- This is the driver for the Berkshire Products ISA-PC Watchdog card. This card simply watches your kernel to make sure it doesn't freeze, @@ -676,7 +658,7 @@ config PCWATCHDOG config MIXCOMWD tristate "Mixcom Watchdog" - depends on WATCHDOG && ISA + depends on ISA ---help--- This is a driver for the Mixcom hardware watchdog cards. This watchdog simply watches your kernel to make sure it doesn't freeze, @@ -690,7 +672,7 @@ config MIXCOMWD config WDT tristate "WDT Watchdog timer" - depends on WATCHDOG && ISA + depends on ISA ---help--- If you have a WDT500P or WDT501P watchdog board, say Y here, otherwise N. It is not possible to probe for this board, which means @@ -720,11 +702,11 @@ config WDT_501 # comment "PCI-based Watchdog Cards" - depends on WATCHDOG && PCI + depends on PCI config PCIPCWATCHDOG tristate "Berkshire Products PCI-PC Watchdog" - depends on WATCHDOG && PCI + depends on PCI ---help--- This is the driver for the Berkshire Products PCI-PC Watchdog card. This card simply watches your kernel to make sure it doesn't freeze, @@ -739,7 +721,7 @@ config PCIPCWATCHDOG config WDTPCI tristate "PCI-WDT500/501 Watchdog timer" - depends on WATCHDOG && PCI + depends on PCI ---help--- If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N. @@ -766,11 +748,11 @@ config WDT_501_PCI # comment "USB-based Watchdog Cards" - depends on WATCHDOG && USB + depends on USB config USBPCWATCHDOG tristate "Berkshire Products USB-PC Watchdog" - depends on WATCHDOG && USB + depends on USB ---help--- This is the driver for the Berkshire Products USB-PC Watchdog card. This card simply watches your kernel to make sure it doesn't freeze, @@ -783,4 +765,4 @@ config USBPCWATCHDOG Most people will say N. -endmenu +endif # WATCHDOG diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index 2cd8ff8d10ac..8bfc00cc7c2b 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -46,7 +46,6 @@ obj-$(CONFIG_IB700_WDT) += ib700wdt.o obj-$(CONFIG_IBMASR) += ibmasr.o obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o -obj-$(CONFIG_I8XX_TCO) += i8xx_tco.o obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o iTCO_vendor_support.o obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o @@ -73,6 +72,7 @@ obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o # MIPS Architecture obj-$(CONFIG_INDYDOG) += indydog.o +obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o # S390 Architecture diff --git a/drivers/char/watchdog/cpu5wdt.c b/drivers/char/watchdog/cpu5wdt.c index bcd7e36ca0aa..d0d45a8b09f0 100644 --- a/drivers/char/watchdog/cpu5wdt.c +++ b/drivers/char/watchdog/cpu5wdt.c @@ -220,17 +220,17 @@ static int __devinit cpu5wdt_init(void) if ( verbose ) printk(KERN_DEBUG PFX "port=0x%x, verbose=%i\n", port, verbose); - if ( (err = misc_register(&cpu5wdt_misc)) < 0 ) { - printk(KERN_ERR PFX "misc_register failed\n"); - goto no_misc; - } - if ( !request_region(port, CPU5WDT_EXTENT, PFX) ) { printk(KERN_ERR PFX "request_region failed\n"); err = -EBUSY; goto no_port; } + if ( (err = misc_register(&cpu5wdt_misc)) < 0 ) { + printk(KERN_ERR PFX "misc_register failed\n"); + goto no_misc; + } + /* watchdog reboot? */ val = inb(port + CPU5WDT_STATUS_REG); val = (val >> 2) & 1; @@ -250,9 +250,9 @@ static int __devinit cpu5wdt_init(void) return 0; -no_port: - misc_deregister(&cpu5wdt_misc); no_misc: + release_region(port, CPU5WDT_EXTENT); +no_port: return err; } diff --git a/drivers/char/watchdog/eurotechwdt.c b/drivers/char/watchdog/eurotechwdt.c index f70387f01b2b..b070324e27a6 100644 --- a/drivers/char/watchdog/eurotechwdt.c +++ b/drivers/char/watchdog/eurotechwdt.c @@ -413,17 +413,10 @@ static int __init eurwdt_init(void) { int ret; - ret = misc_register(&eurwdt_miscdev); - if (ret) { - printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n", - WATCHDOG_MINOR); - goto out; - } - ret = request_irq(irq, eurwdt_interrupt, IRQF_DISABLED, "eurwdt", NULL); if(ret) { printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq); - goto outmisc; + goto out; } if (!request_region(io, 2, "eurwdt")) { @@ -438,6 +431,13 @@ static int __init eurwdt_init(void) goto outreg; } + ret = misc_register(&eurwdt_miscdev); + if (ret) { + printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n", + WATCHDOG_MINOR); + goto outreboot; + } + eurwdt_unlock_chip(); ret = 0; @@ -448,14 +448,14 @@ static int __init eurwdt_init(void) out: return ret; +outreboot: + unregister_reboot_notifier(&eurwdt_notifier); + outreg: release_region(io, 2); outirq: free_irq(irq, NULL); - -outmisc: - misc_deregister(&eurwdt_miscdev); goto out; } diff --git a/drivers/char/watchdog/i8xx_tco.c b/drivers/char/watchdog/i8xx_tco.c deleted file mode 100644 index a62ef48a15ae..000000000000 --- a/drivers/char/watchdog/i8xx_tco.c +++ /dev/null @@ -1,571 +0,0 @@ -/* - * i8xx_tco: TCO timer driver for i8xx chipsets - * - * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved. - * http://www.kernelconcepts.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. - * - * Neither kernel concepts nor Nils Faerber admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de> - * developed for - * Jentro AG, Haar/Munich (Germany) - * - * TCO timer driver for i8xx chipsets - * based on softdog.c by Alan Cox <alan@redhat.com> - * - * The TCO timer is implemented in the following I/O controller hubs: - * (See the intel documentation on http://developer.intel.com.) - * 82801AA (ICH) : document number 290655-003, 290677-014, - * 82801AB (ICHO) : document number 290655-003, 290677-014, - * 82801BA (ICH2) : document number 290687-002, 298242-027, - * 82801BAM (ICH2-M) : document number 290687-002, 298242-027, - * 82801CA (ICH3-S) : document number 290733-003, 290739-013, - * 82801CAM (ICH3-M) : document number 290716-001, 290718-007, - * 82801DB (ICH4) : document number 290744-001, 290745-020, - * 82801DBM (ICH4-M) : document number 252337-001, 252663-005, - * 82801E (C-ICH) : document number 273599-001, 273645-002, - * 82801EB (ICH5) : document number 252516-001, 252517-003, - * 82801ER (ICH5R) : document number 252516-001, 252517-003, - * - * 20000710 Nils Faerber - * Initial Version 0.01 - * 20000728 Nils Faerber - * 0.02 Fix for SMI_EN->TCO_EN bit, some cleanups - * 20011214 Matt Domsch <Matt_Domsch@dell.com> - * 0.03 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - * Didn't add timeout option as i810_margin already exists. - * 20020224 Joel Becker, Wim Van Sebroeck - * 0.04 Support for 82801CA(M) chipset, timer margin needs to be > 3, - * add support for WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT. - * 20020412 Rob Radez <rob@osinvestor.com>, Wim Van Sebroeck - * 0.05 Fix possible timer_alive race, add expect close support, - * clean up ioctls (WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS and - * WDIOC_SETOPTIONS), made i810tco_getdevice __init, - * removed boot_status, removed tco_timer_read, - * added support for 82801DB and 82801E chipset, - * added support for 82801EB and 8280ER chipset, - * general cleanup. - * 20030921 Wim Van Sebroeck <wim@iguana.be> - * 0.06 change i810_margin to heartbeat, use module_param, - * added notify system support, renamed module to i8xx_tco. - * 20050128 Wim Van Sebroeck <wim@iguana.be> - * 0.07 Added support for the ICH4-M, ICH6, ICH6R, ICH6-M, ICH6W and ICH6RW - * chipsets. Also added support for the "undocumented" ICH7 chipset. - * 20050807 Wim Van Sebroeck <wim@iguana.be> - * 0.08 Make sure that the watchdog is only "armed" when started. - * (Kernel Bug 4251) - * 20060416 Wim Van Sebroeck <wim@iguana.be> - * 0.09 Remove support for the ICH6, ICH6R, ICH6-M, ICH6W and ICH6RW and - * ICH7 chipsets. (See Kernel Bug 6031 - other code will support these - * chipsets) - */ - -/* - * Includes, defines, variables, module parameters, ... - */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/types.h> -#include <linux/miscdevice.h> -#include <linux/watchdog.h> -#include <linux/notifier.h> -#include <linux/reboot.h> -#include <linux/init.h> -#include <linux/fs.h> -#include <linux/pci.h> -#include <linux/ioport.h> - -#include <asm/uaccess.h> -#include <asm/io.h> - -#include "i8xx_tco.h" - -/* Module and version information */ -#define TCO_VERSION "0.09" -#define TCO_MODULE_NAME "i8xx TCO timer" -#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION -#define PFX TCO_MODULE_NAME ": " - -/* internal variables */ -static unsigned int ACPIBASE; -static spinlock_t tco_lock; /* Guards the hardware */ -static unsigned long timer_alive; -static char tco_expect_close; -static struct pci_dev *i8xx_tco_pci; - -/* module parameters */ -#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat (2<heartbeat<39) */ -static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); - -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -/* - * Some TCO specific functions - */ - -static inline unsigned char seconds_to_ticks(int seconds) -{ - /* the internal timer is stored as ticks which decrement - * every 0.6 seconds */ - return (seconds * 10) / 6; -} - -static int tco_timer_start (void) -{ - unsigned char val; - - spin_lock(&tco_lock); - - /* disable chipset's NO_REBOOT bit */ - pci_read_config_byte (i8xx_tco_pci, 0xd4, &val); - val &= 0xfd; - pci_write_config_byte (i8xx_tco_pci, 0xd4, val); - - /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */ - val = inb (TCO1_CNT + 1); - val &= 0xf7; - outb (val, TCO1_CNT + 1); - val = inb (TCO1_CNT + 1); - - spin_unlock(&tco_lock); - - if (val & 0x08) - return -1; - return 0; -} - -static int tco_timer_stop (void) -{ - unsigned char val, val1; - - spin_lock(&tco_lock); - /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */ - val = inb (TCO1_CNT + 1); - val |= 0x08; - outb (val, TCO1_CNT + 1); - val = inb (TCO1_CNT + 1); - - /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ - pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1); - val1 |= 0x02; - pci_write_config_byte (i8xx_tco_pci, 0xd4, val1); - - spin_unlock(&tco_lock); - - if ((val & 0x08) == 0) - return -1; - return 0; -} - -static int tco_timer_keepalive (void) -{ - spin_lock(&tco_lock); - /* Reload the timer by writing to the TCO Timer Reload register */ - outb (0x01, TCO1_RLD); - spin_unlock(&tco_lock); - return 0; -} - -static int tco_timer_set_heartbeat (int t) -{ - unsigned char val; - unsigned char tmrval; - - tmrval = seconds_to_ticks(t); - /* from the specs: */ - /* "Values of 0h-3h are ignored and should not be attempted" */ - if (tmrval > 0x3f || tmrval < 0x04) - return -EINVAL; - - /* Write new heartbeat to watchdog */ - spin_lock(&tco_lock); - val = inb (TCO1_TMR); - val &= 0xc0; - val |= tmrval; - outb (val, TCO1_TMR); - val = inb (TCO1_TMR); - spin_unlock(&tco_lock); - - if ((val & 0x3f) != tmrval) - return -EINVAL; - - heartbeat = t; - return 0; -} - -static int tco_timer_get_timeleft (int *time_left) -{ - unsigned char val; - - spin_lock(&tco_lock); - - /* read the TCO Timer */ - val = inb (TCO1_RLD); - val &= 0x3f; - - spin_unlock(&tco_lock); - - *time_left = (int)((val * 6) / 10); - - return 0; -} - -/* - * /dev/watchdog handling - */ - -static int i8xx_tco_open (struct inode *inode, struct file *file) -{ - /* /dev/watchdog can only be opened once */ - if (test_and_set_bit(0, &timer_alive)) - return -EBUSY; - - /* - * Reload and activate timer - */ - tco_timer_keepalive (); - tco_timer_start (); - return nonseekable_open(inode, file); -} - -static int i8xx_tco_release (struct inode *inode, struct file *file) -{ - /* - * Shut off the timer. - */ - if (tco_expect_close == 42) { - tco_timer_stop (); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); - tco_timer_keepalive (); - } - clear_bit(0, &timer_alive); - tco_expect_close = 0; - return 0; -} - -static ssize_t i8xx_tco_write (struct file *file, const char __user *data, - size_t len, loff_t * ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if (len) { - if (!nowayout) { - size_t i; - - /* note: just in case someone wrote the magic character - * five months ago... */ - tco_expect_close = 0; - - /* scan to see whether or not we got the magic character */ - for (i = 0; i != len; i++) { - char c; - if(get_user(c, data+i)) - return -EFAULT; - if (c == 'V') - tco_expect_close = 42; - } - } - - /* someone wrote to us, we should reload the timer */ - tco_timer_keepalive (); - } - return len; -} - -static int i8xx_tco_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int new_options, retval = -EINVAL; - int new_heartbeat; - int time_left; - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, - .firmware_version = 0, - .identity = TCO_MODULE_NAME, - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &ident, - sizeof (ident)) ? -EFAULT : 0; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user (0, p); - - case WDIOC_KEEPALIVE: - tco_timer_keepalive (); - return 0; - - case WDIOC_SETOPTIONS: - { - if (get_user (new_options, p)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) { - tco_timer_stop (); - retval = 0; - } - - if (new_options & WDIOS_ENABLECARD) { - tco_timer_keepalive (); - tco_timer_start (); - retval = 0; - } - - return retval; - } - - case WDIOC_SETTIMEOUT: - { - if (get_user(new_heartbeat, p)) - return -EFAULT; - - if (tco_timer_set_heartbeat(new_heartbeat)) - return -EINVAL; - - tco_timer_keepalive (); - /* Fall */ - } - - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, p); - - case WDIOC_GETTIMELEFT: - { - if (tco_timer_get_timeleft(&time_left)) - return -EINVAL; - - return put_user(time_left, p); - } - - default: - return -ENOTTY; - } -} - -/* - * Notify system - */ - -static int i8xx_tco_notify_sys (struct notifier_block *this, unsigned long code, void *unused) -{ - if (code==SYS_DOWN || code==SYS_HALT) { - /* Turn the WDT off */ - tco_timer_stop (); - } - - return NOTIFY_DONE; -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations i8xx_tco_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = i8xx_tco_write, - .ioctl = i8xx_tco_ioctl, - .open = i8xx_tco_open, - .release = i8xx_tco_release, -}; - -static struct miscdevice i8xx_tco_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &i8xx_tco_fops, -}; - -static struct notifier_block i8xx_tco_notifier = { - .notifier_call = i8xx_tco_notify_sys, -}; - -/* - * Data for PCI driver interface - * - * This data only exists for exporting the supported - * PCI ids via MODULE_DEVICE_TABLE. We do not actually - * register a pci_driver, because someone else might one day - * want to register another driver on the same PCI id. - */ -static struct pci_device_id i8xx_tco_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1) }, - { }, /* End of list */ -}; -MODULE_DEVICE_TABLE (pci, i8xx_tco_pci_tbl); - -/* - * Init & exit routines - */ - -static unsigned char __init i8xx_tco_getdevice (void) -{ - struct pci_dev *dev = NULL; - u8 val1, val2; - u16 badr; - /* - * Find the PCI device - */ - - for_each_pci_dev(dev) - if (pci_match_id(i8xx_tco_pci_tbl, dev)) { - i8xx_tco_pci = dev; - break; - } - - if (i8xx_tco_pci) { - /* - * Find the ACPI base I/O address which is the base - * for the TCO registers (TCOBASE=ACPIBASE + 0x60) - * ACPIBASE is bits [15:7] from 0x40-0x43 - */ - pci_read_config_byte (i8xx_tco_pci, 0x40, &val1); - pci_read_config_byte (i8xx_tco_pci, 0x41, &val2); - badr = ((val2 << 1) | (val1 >> 7)) << 7; - ACPIBASE = badr; - /* Something's wrong here, ACPIBASE has to be set */ - if (badr == 0x0001 || badr == 0x0000) { - printk (KERN_ERR PFX "failed to get TCOBASE address\n"); - pci_dev_put(i8xx_tco_pci); - return 0; - } - - /* Check chipset's NO_REBOOT bit */ - pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1); - if (val1 & 0x02) { - val1 &= 0xfd; - pci_write_config_byte (i8xx_tco_pci, 0xd4, val1); - pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1); - if (val1 & 0x02) { - printk (KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot disabled by hardware\n"); - pci_dev_put(i8xx_tco_pci); - return 0; /* Cannot reset NO_REBOOT bit */ - } - } - /* Disable reboots untill the watchdog starts */ - val1 |= 0x02; - pci_write_config_byte (i8xx_tco_pci, 0xd4, val1); - - /* Set the TCO_EN bit in SMI_EN register */ - if (!request_region (SMI_EN + 1, 1, "i8xx TCO")) { - printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", - SMI_EN + 1); - pci_dev_put(i8xx_tco_pci); - return 0; - } - val1 = inb (SMI_EN + 1); - val1 &= 0xdf; - outb (val1, SMI_EN + 1); - release_region (SMI_EN + 1, 1); - return 1; - } - return 0; -} - -static int __init watchdog_init (void) -{ - int ret; - - spin_lock_init(&tco_lock); - - /* Check whether or not the hardware watchdog is there */ - if (!i8xx_tco_getdevice () || i8xx_tco_pci == NULL) - return -ENODEV; - - if (!request_region (TCOBASE, 0x10, "i8xx TCO")) { - printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", - TCOBASE); - ret = -EIO; - goto out; - } - - /* Clear out the (probably old) status */ - outb (0, TCO1_STS); - outb (3, TCO2_STS); - - /* Check that the heartbeat value is within it's range ; if not reset to the default */ - if (tco_timer_set_heartbeat (heartbeat)) { - heartbeat = WATCHDOG_HEARTBEAT; - tco_timer_set_heartbeat (heartbeat); - printk(KERN_INFO PFX "heartbeat value must be 2<heartbeat<39, using %d\n", - heartbeat); - } - - ret = register_reboot_notifier(&i8xx_tco_notifier); - if (ret != 0) { - printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", - ret); - goto unreg_region; - } - - ret = misc_register(&i8xx_tco_miscdev); - if (ret != 0) { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - goto unreg_notifier; - } - - tco_timer_stop (); - - printk (KERN_INFO PFX "initialized (0x%04x). heartbeat=%d sec (nowayout=%d)\n", - TCOBASE, heartbeat, nowayout); - - return 0; - -unreg_notifier: - unregister_reboot_notifier(&i8xx_tco_notifier); -unreg_region: - release_region (TCOBASE, 0x10); -out: - pci_dev_put(i8xx_tco_pci); - return ret; -} - -static void __exit watchdog_cleanup (void) -{ - /* Stop the timer before we leave */ - if (!nowayout) - tco_timer_stop (); - - /* Deregister */ - misc_deregister (&i8xx_tco_miscdev); - unregister_reboot_notifier(&i8xx_tco_notifier); - release_region (TCOBASE, 0x10); - - pci_dev_put(i8xx_tco_pci); -} - -module_init(watchdog_init); -module_exit(watchdog_cleanup); - -MODULE_AUTHOR("Nils Faerber"); -MODULE_DESCRIPTION("TCO timer driver for i8xx chipsets"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/i8xx_tco.h b/drivers/char/watchdog/i8xx_tco.h deleted file mode 100644 index cc14eb8ac3d6..000000000000 --- a/drivers/char/watchdog/i8xx_tco.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * i8xx_tco: TCO timer driver for i8xx chipsets - * - * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved. - * http://www.kernelconcepts.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. - * - * Neither kernel concepts nor Nils Faerber admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de> - * developed for - * Jentro AG, Haar/Munich (Germany) - * - * TCO timer driver for i8xx chipsets - * based on softdog.c by Alan Cox <alan@redhat.com> - * - * For history and the complete list of supported I/O Controller Hub's - * see i8xx_tco.c - */ - - -/* - * Some address definitions for the TCO - */ - -#define TCOBASE ACPIBASE + 0x60 /* TCO base address */ -#define TCO1_RLD TCOBASE + 0x00 /* TCO Timer Reload and Current Value */ -#define TCO1_TMR TCOBASE + 0x01 /* TCO Timer Initial Value */ -#define TCO1_DAT_IN TCOBASE + 0x02 /* TCO Data In Register */ -#define TCO1_DAT_OUT TCOBASE + 0x03 /* TCO Data Out Register */ -#define TCO1_STS TCOBASE + 0x04 /* TCO1 Status Register */ -#define TCO2_STS TCOBASE + 0x06 /* TCO2 Status Register */ -#define TCO1_CNT TCOBASE + 0x08 /* TCO1 Control Register */ -#define TCO2_CNT TCOBASE + 0x0a /* TCO2 Control Register */ - -#define SMI_EN ACPIBASE + 0x30 /* SMI Control and Enable Register */ diff --git a/drivers/char/watchdog/ibmasr.c b/drivers/char/watchdog/ibmasr.c index 8195f5023d85..94155f6136c2 100644 --- a/drivers/char/watchdog/ibmasr.c +++ b/drivers/char/watchdog/ibmasr.c @@ -367,18 +367,17 @@ static int __init ibmasr_init(void) if (!asr_type) return -ENODEV; + rc = asr_get_base_address(); + if (rc) + return rc; + rc = misc_register(&asr_miscdev); if (rc < 0) { + release_region(asr_base, asr_length); printk(KERN_ERR PFX "failed to register misc device\n"); return rc; } - rc = asr_get_base_address(); - if (rc) { - misc_deregister(&asr_miscdev); - return rc; - } - return 0; } diff --git a/drivers/char/watchdog/machzwd.c b/drivers/char/watchdog/machzwd.c index 76c7fa37fa6c..a0d27160c80e 100644 --- a/drivers/char/watchdog/machzwd.c +++ b/drivers/char/watchdog/machzwd.c @@ -440,13 +440,6 @@ static int __init zf_init(void) spin_lock_init(&zf_lock); spin_lock_init(&zf_port_lock); - ret = misc_register(&zf_miscdev); - if (ret){ - printk(KERN_ERR "can't misc_register on minor=%d\n", - WATCHDOG_MINOR); - goto out; - } - if(!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")){ printk(KERN_ERR "cannot reserve I/O ports at %d\n", ZF_IOBASE); @@ -461,16 +454,23 @@ static int __init zf_init(void) goto no_reboot; } + ret = misc_register(&zf_miscdev); + if (ret){ + printk(KERN_ERR "can't misc_register on minor=%d\n", + WATCHDOG_MINOR); + goto no_misc; + } + zf_set_status(0); zf_set_control(0); return 0; +no_misc: + unregister_reboot_notifier(&zf_notifier); no_reboot: release_region(ZF_IOBASE, 3); no_region: - misc_deregister(&zf_miscdev); -out: return ret; } diff --git a/drivers/char/watchdog/mtx-1_wdt.c b/drivers/char/watchdog/mtx-1_wdt.c new file mode 100644 index 000000000000..419ab445c944 --- /dev/null +++ b/drivers/char/watchdog/mtx-1_wdt.c @@ -0,0 +1,238 @@ +/* + * Driver for the MTX-1 Watchdog. + * + * (C) Copyright 2005 4G Systems <info@4g-systems.biz>, All Rights Reserved. + * http://www.4g-systems.biz + * + * (C) Copyright 2007 OpenWrt.org, Florian Fainelli <florian@openwrt.org> + * + * 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. + * + * Neither Michael Stickel nor 4G Systems admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 2005 4G Systems <info@4g-systems.biz> + * + * Release 0.01. + * Author: Michael Stickel michael.stickel@4g-systems.biz + * + * Release 0.02. + * Author: Florian Fainelli florian@openwrt.org + * use the Linux watchdog/timer APIs + * + * The Watchdog is configured to reset the MTX-1 + * if it is not triggered for 100 seconds. + * It should not be triggered more often than 1.6 seconds. + * + * A timer triggers the watchdog every 5 seconds, until + * it is opened for the first time. After the first open + * it MUST be triggered every 2..95 seconds. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/timer.h> +#include <linux/completion.h> +#include <linux/jiffies.h> +#include <linux/watchdog.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +#include <asm/mach-au1x00/au1000.h> + +#define MTX1_WDT_INTERVAL (5 * HZ) + +static int ticks = 100 * HZ; + +static struct { + struct completion stop; + volatile int running; + struct timer_list timer; + volatile int queue; + int default_ticks; + unsigned long inuse; +} mtx1_wdt_device; + +static void mtx1_wdt_trigger(unsigned long unused) +{ + u32 tmp; + + if (mtx1_wdt_device.running) + ticks--; + /* + * toggle GPIO2_15 + */ + tmp = au_readl(GPIO2_DIR); + tmp = (tmp & ~(1<<15)) | ((~tmp) & (1<<15)); + au_writel (tmp, GPIO2_DIR); + + if (mtx1_wdt_device.queue && ticks) + mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL); + else { + complete(&mtx1_wdt_device.stop); + } +} + +static void mtx1_wdt_reset(void) +{ + ticks = mtx1_wdt_device.default_ticks; +} + + +static void mtx1_wdt_start(void) +{ + if (!mtx1_wdt_device.queue) { + mtx1_wdt_device.queue = 1; + au_writel (au_readl(GPIO2_DIR) | (u32)(1<<15), GPIO2_DIR); + mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL); + } + mtx1_wdt_device.running++; +} + +static int mtx1_wdt_stop(void) +{ + if (mtx1_wdt_device.queue) { + mtx1_wdt_device.queue = 0; + au_writel (au_readl(GPIO2_DIR) & ~((u32)(1<<15)), GPIO2_DIR); + } + + ticks = mtx1_wdt_device.default_ticks; + + return 0; +} + +/* Filesystem functions */ + +static int mtx1_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &mtx1_wdt_device.inuse)) + return -EBUSY; + + return nonseekable_open(inode, file); +} + + +static int mtx1_wdt_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &mtx1_wdt_device.inuse); + return 0; +} + +static int mtx1_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + unsigned int value; + static struct watchdog_info ident = + { + .options = WDIOF_CARDRESET, + .identity = "MTX-1 WDT", + }; + + switch(cmd) { + case WDIOC_KEEPALIVE: + mtx1_wdt_reset(); + break; + case WDIOC_GETSTATUS: + if ( copy_to_user(argp, &value, sizeof(int)) ) + return -EFAULT; + break; + case WDIOC_GETSUPPORT: + if ( copy_to_user(argp, &ident, sizeof(ident)) ) + return -EFAULT; + break; + case WDIOC_SETOPTIONS: + if ( copy_from_user(&value, argp, sizeof(int)) ) + return -EFAULT; + switch(value) { + case WDIOS_ENABLECARD: + mtx1_wdt_start(); + break; + case WDIOS_DISABLECARD: + return mtx1_wdt_stop(); + default: + return -EINVAL; + } + break; + default: + return -ENOTTY; + } + return 0; +} + + +static ssize_t mtx1_wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + if (!count) + return -EIO; + + mtx1_wdt_reset(); + return count; +} + +static struct file_operations mtx1_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = mtx1_wdt_ioctl, + .open = mtx1_wdt_open, + .write = mtx1_wdt_write, + .release = mtx1_wdt_release +}; + + +static struct miscdevice mtx1_wdt_misc = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mtx1_wdt_fops +}; + + +static int __init mtx1_wdt_init(void) +{ + int ret; + + if ((ret = misc_register(&mtx1_wdt_misc)) < 0) { + printk(KERN_ERR " mtx-1_wdt : failed to register\n"); + return ret; + } + + init_completion(&mtx1_wdt_device.stop); + mtx1_wdt_device.queue = 0; + + clear_bit(0, &mtx1_wdt_device.inuse); + + setup_timer(&mtx1_wdt_device.timer, mtx1_wdt_trigger, 0L); + + mtx1_wdt_device.default_ticks = ticks; + + mtx1_wdt_start(); + + printk(KERN_INFO "MTX-1 Watchdog driver\n"); + + return 0; +} + +static void __exit mtx1_wdt_exit(void) +{ + if (mtx1_wdt_device.queue) { + mtx1_wdt_device.queue = 0; + wait_for_completion(&mtx1_wdt_device.stop); + } + misc_deregister(&mtx1_wdt_misc); +} + +module_init(mtx1_wdt_init); +module_exit(mtx1_wdt_exit); + +MODULE_AUTHOR("Michael Stickel, Florian Fainelli"); +MODULE_DESCRIPTION("Driver for the MTX-1 watchdog"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c index 6e8b5705b5b7..7b41434fac8c 100644 --- a/drivers/char/watchdog/pcwd.c +++ b/drivers/char/watchdog/pcwd.c @@ -59,10 +59,10 @@ #include <linux/jiffies.h> /* For jiffies stuff */ #include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ #include <linux/watchdog.h> /* For the watchdog specific items */ -#include <linux/notifier.h> /* For notifier support */ -#include <linux/reboot.h> /* For reboot_notifier stuff */ +#include <linux/reboot.h> /* For kernel_power_off() */ #include <linux/init.h> /* For __init/__exit/... */ #include <linux/fs.h> /* For file operations */ +#include <linux/isa.h> /* For isa devices */ #include <linux/ioport.h> /* For io-port access */ #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ @@ -70,8 +70,8 @@ #include <asm/io.h> /* For inb/outb/... */ /* Module and version information */ -#define WATCHDOG_VERSION "1.18" -#define WATCHDOG_DATE "21 Jan 2007" +#define WATCHDOG_VERSION "1.20" +#define WATCHDOG_DATE "18 Feb 2007" #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog" #define WATCHDOG_NAME "pcwd" #define PFX WATCHDOG_NAME ": " @@ -89,6 +89,15 @@ #define PCWD_REVISION_C 2 /* + * These are the auto-probe addresses available. + * + * Revision A only uses ports 0x270 and 0x370. Revision C introduced 0x350. + * Revision A has an address range of 2 addresses, while Revision C has 4. + */ +#define PCWD_ISA_NR_CARDS 3 +static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; + +/* * These are the defines that describe the control status bits for the * PCI-PC Watchdog card. */ @@ -485,7 +494,7 @@ static int pcwd_get_status(int *status) if (control_status & WD_T110) { *status |= WDIOF_OVERHEAT; if (temp_panic) { - printk (KERN_INFO PFX "Temperature overheat trip!\n"); + printk(KERN_INFO PFX "Temperature overheat trip!\n"); kernel_power_off(); /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ } @@ -497,7 +506,7 @@ static int pcwd_get_status(int *status) if (control_status & WD_REVC_TTRP) { *status |= WDIOF_OVERHEAT; if (temp_panic) { - printk (KERN_INFO PFX "Temperature overheat trip!\n"); + printk(KERN_INFO PFX "Temperature overheat trip!\n"); kernel_power_off(); /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ } @@ -734,20 +743,6 @@ static int pcwd_temp_close(struct inode *inode, struct file *file) } /* - * Notify system - */ - -static int pcwd_notify_sys(struct notifier_block *this, unsigned long code, void *unused) -{ - if (code==SYS_DOWN || code==SYS_HALT) { - /* Turn the WDT off */ - pcwd_stop(); - } - - return NOTIFY_DONE; -} - -/* * Kernel Interfaces */ @@ -780,10 +775,6 @@ static struct miscdevice temp_miscdev = { .fops = &pcwd_temp_fops, }; -static struct notifier_block pcwd_notifier = { - .notifier_call = pcwd_notify_sys, -}; - /* * Init & exit routines */ @@ -803,10 +794,67 @@ static inline int get_revision(void) return r; } -static int __devinit pcwatchdog_init(int base_addr) +/* + * The ISA cards have a heartbeat bit in one of the registers, which + * register is card dependent. The heartbeat bit is monitored, and if + * found, is considered proof that a Berkshire card has been found. + * The initial rate is once per second at board start up, then twice + * per second for normal operation. + */ +static int __devinit pcwd_isa_match(struct device *dev, unsigned int id) +{ + int base_addr=pcwd_ioports[id]; + int port0, last_port0; /* Reg 0, in case it's REV A */ + int port1, last_port1; /* Register 1 for REV C cards */ + int i; + int retval; + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "pcwd_isa_match id=%d\n", + id); + + if (!request_region (base_addr, 4, "PCWD")) { + printk(KERN_INFO PFX "Port 0x%04x unavailable\n", base_addr); + return 0; + } + + retval = 0; + + port0 = inb_p(base_addr); /* For REV A boards */ + port1 = inb_p(base_addr + 1); /* For REV C boards */ + if (port0 != 0xff || port1 != 0xff) { + /* Not an 'ff' from a floating bus, so must be a card! */ + for (i = 0; i < 4; ++i) { + + msleep(500); + + last_port0 = port0; + last_port1 = port1; + + port0 = inb_p(base_addr); + port1 = inb_p(base_addr + 1); + + /* Has either hearbeat bit changed? */ + if ((port0 ^ last_port0) & WD_HRTBT || + (port1 ^ last_port1) & WD_REVC_HRBT) { + retval = 1; + break; + } + } + } + release_region (base_addr, 4); + + return retval; +} + +static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id) { int ret; + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "pcwd_isa_probe id=%d\n", + id); + cards_found++; if (cards_found == 1) printk(KERN_INFO PFX "v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER); @@ -816,11 +864,13 @@ static int __devinit pcwatchdog_init(int base_addr) return -ENODEV; } - if (base_addr == 0x0000) { + if (pcwd_ioports[id] == 0x0000) { printk(KERN_ERR PFX "No I/O-Address for card detected\n"); return -ENODEV; } - pcwd_private.io_addr = base_addr; + pcwd_private.io_addr = pcwd_ioports[id]; + + spin_lock_init(&pcwd_private.io_lock); /* Check card's revision */ pcwd_private.revision = get_revision(); @@ -828,8 +878,8 @@ static int __devinit pcwatchdog_init(int base_addr) if (!request_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) { printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", pcwd_private.io_addr); - pcwd_private.io_addr = 0x0000; - return -EIO; + ret=-EIO; + goto error_request_region; } /* Initial variables */ @@ -865,24 +915,12 @@ static int __devinit pcwatchdog_init(int base_addr) WATCHDOG_HEARTBEAT); } - ret = register_reboot_notifier(&pcwd_notifier); - if (ret) { - printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", - ret); - release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); - pcwd_private.io_addr = 0x0000; - return ret; - } - if (pcwd_private.supports_temp) { ret = misc_register(&temp_miscdev); if (ret) { printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", TEMP_MINOR, ret); - unregister_reboot_notifier(&pcwd_notifier); - release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); - pcwd_private.io_addr = 0x0000; - return ret; + goto error_misc_register_temp; } } @@ -890,22 +928,34 @@ static int __devinit pcwatchdog_init(int base_addr) if (ret) { printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, ret); - if (pcwd_private.supports_temp) - misc_deregister(&temp_miscdev); - unregister_reboot_notifier(&pcwd_notifier); - release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); - pcwd_private.io_addr = 0x0000; - return ret; + goto error_misc_register_watchdog; } printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); return 0; + +error_misc_register_watchdog: + if (pcwd_private.supports_temp) + misc_deregister(&temp_miscdev); +error_misc_register_temp: + release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); +error_request_region: + pcwd_private.io_addr = 0x0000; + cards_found--; + return ret; } -static void __devexit pcwatchdog_exit(void) +static int __devexit pcwd_isa_remove(struct device *dev, unsigned int id) { + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "pcwd_isa_remove id=%d\n", + id); + + if (!pcwd_private.io_addr) + return 1; + /* Disable the board */ if (!nowayout) pcwd_stop(); @@ -914,102 +964,50 @@ static void __devexit pcwatchdog_exit(void) misc_deregister(&pcwd_miscdev); if (pcwd_private.supports_temp) misc_deregister(&temp_miscdev); - unregister_reboot_notifier(&pcwd_notifier); release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); pcwd_private.io_addr = 0x0000; cards_found--; + + return 0; } -/* - * The ISA cards have a heartbeat bit in one of the registers, which - * register is card dependent. The heartbeat bit is monitored, and if - * found, is considered proof that a Berkshire card has been found. - * The initial rate is once per second at board start up, then twice - * per second for normal operation. - */ -static int __init pcwd_checkcard(int base_addr) +static void pcwd_isa_shutdown(struct device *dev, unsigned int id) { - int port0, last_port0; /* Reg 0, in case it's REV A */ - int port1, last_port1; /* Register 1 for REV C cards */ - int i; - int retval; - - if (!request_region (base_addr, 4, "PCWD")) { - printk (KERN_INFO PFX "Port 0x%04x unavailable\n", base_addr); - return 0; - } - - retval = 0; - - port0 = inb_p(base_addr); /* For REV A boards */ - port1 = inb_p(base_addr + 1); /* For REV C boards */ - if (port0 != 0xff || port1 != 0xff) { - /* Not an 'ff' from a floating bus, so must be a card! */ - for (i = 0; i < 4; ++i) { - - msleep(500); - - last_port0 = port0; - last_port1 = port1; - - port0 = inb_p(base_addr); - port1 = inb_p(base_addr + 1); - - /* Has either hearbeat bit changed? */ - if ((port0 ^ last_port0) & WD_HRTBT || - (port1 ^ last_port1) & WD_REVC_HRBT) { - retval = 1; - break; - } - } - } - release_region (base_addr, 4); + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "pcwd_isa_shutdown id=%d\n", + id); - return retval; + pcwd_stop(); } -/* - * These are the auto-probe addresses available. - * - * Revision A only uses ports 0x270 and 0x370. Revision C introduced 0x350. - * Revision A has an address range of 2 addresses, while Revision C has 4. - */ -static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; +static struct isa_driver pcwd_isa_driver = { + .match = pcwd_isa_match, + .probe = pcwd_isa_probe, + .remove = __devexit_p(pcwd_isa_remove), + .shutdown = pcwd_isa_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = WATCHDOG_NAME, + }, +}; static int __init pcwd_init_module(void) { - int i, found = 0; - - spin_lock_init(&pcwd_private.io_lock); - - for (i = 0; pcwd_ioports[i] != 0; i++) { - if (pcwd_checkcard(pcwd_ioports[i])) { - if (!(pcwatchdog_init(pcwd_ioports[i]))) - found++; - } - } - - if (!found) { - printk (KERN_INFO PFX "No card detected, or port not available\n"); - return -ENODEV; - } - - return 0; + return isa_register_driver(&pcwd_isa_driver, PCWD_ISA_NR_CARDS); } static void __exit pcwd_cleanup_module(void) { - if (pcwd_private.io_addr) - pcwatchdog_exit(); - + isa_unregister_driver(&pcwd_isa_driver); printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); } module_init(pcwd_init_module); module_exit(pcwd_cleanup_module); -MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>"); +MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, Wim Van Sebroeck <wim@iguana.be>"); MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver"); +MODULE_VERSION(WATCHDOG_VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); MODULE_ALIAS_MISCDEV(TEMP_MINOR); diff --git a/drivers/char/watchdog/pcwd_usb.c b/drivers/char/watchdog/pcwd_usb.c index 31037f9c9ffe..1e7a6719d5ba 100644 --- a/drivers/char/watchdog/pcwd_usb.c +++ b/drivers/char/watchdog/pcwd_usb.c @@ -146,7 +146,7 @@ struct usb_pcwd_private { atomic_t cmd_received; /* true if we received a report after a command */ int exists; /* Wether or not the device exists */ - struct semaphore sem; /* locks this structure */ + struct mutex mtx; /* locks this structure */ }; static struct usb_pcwd_private *usb_pcwd_device; @@ -635,7 +635,7 @@ static int usb_pcwd_probe(struct usb_interface *interface, const struct usb_devi usb_pcwd_device = usb_pcwd; - init_MUTEX (&usb_pcwd->sem); + mutex_init(&usb_pcwd->mtx); usb_pcwd->udev = udev; usb_pcwd->interface = interface; usb_pcwd->interface_number = iface_desc->desc.bInterfaceNumber; @@ -763,7 +763,7 @@ static void usb_pcwd_disconnect(struct usb_interface *interface) usb_pcwd = usb_get_intfdata (interface); usb_set_intfdata (interface, NULL); - down (&usb_pcwd->sem); + mutex_lock(&usb_pcwd->mtx); /* Stop the timer before we leave */ if (!nowayout) @@ -777,7 +777,7 @@ static void usb_pcwd_disconnect(struct usb_interface *interface) misc_deregister(&usb_pcwd_temperature_miscdev); unregister_reboot_notifier(&usb_pcwd_notifier); - up (&usb_pcwd->sem); + mutex_unlock(&usb_pcwd->mtx); /* Delete the USB PCWD device */ usb_pcwd_delete(usb_pcwd); diff --git a/drivers/char/watchdog/s3c2410_wdt.c b/drivers/char/watchdog/s3c2410_wdt.c index dff6cb5dc9a7..20fa29ca7404 100644 --- a/drivers/char/watchdog/s3c2410_wdt.c +++ b/drivers/char/watchdog/s3c2410_wdt.c @@ -379,14 +379,14 @@ static int s3c2410wdt_probe(struct platform_device *pdev) DBG("probe: mapped wdt_base=%p\n", wdt_base); - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { + wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (wdt_irq == NULL) { printk(KERN_INFO PFX "failed to get irq resource\n"); ret = -ENOENT; goto err_map; } - ret = request_irq(res->start, s3c2410wdt_irq, 0, pdev->name, pdev); + ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev); if (ret != 0) { printk(KERN_INFO PFX "failed to install irq (%d)\n", ret); goto err_map; diff --git a/drivers/char/watchdog/sbc8360.c b/drivers/char/watchdog/sbc8360.c index 67ae42685e75..285d85289532 100644 --- a/drivers/char/watchdog/sbc8360.c +++ b/drivers/char/watchdog/sbc8360.c @@ -333,18 +333,17 @@ static int __init sbc8360_init(void) int res; unsigned long int mseconds = 60000; - spin_lock_init(&sbc8360_lock); - res = misc_register(&sbc8360_miscdev); - if (res) { - printk(KERN_ERR PFX "failed to register misc device\n"); - goto out_nomisc; + if (timeout < 0 || timeout > 63) { + printk(KERN_ERR PFX "Invalid timeout index (must be 0-63).\n"); + res = -EINVAL; + goto out; } if (!request_region(SBC8360_ENABLE, 1, "SBC8360")) { printk(KERN_ERR PFX "ENABLE method I/O %X is not available.\n", SBC8360_ENABLE); res = -EIO; - goto out_noenablereg; + goto out; } if (!request_region(SBC8360_BASETIME, 1, "SBC8360")) { printk(KERN_ERR PFX @@ -360,10 +359,11 @@ static int __init sbc8360_init(void) goto out_noreboot; } - if (timeout < 0 || timeout > 63) { - printk(KERN_ERR PFX "Invalid timeout index (must be 0-63).\n"); - res = -EINVAL; - goto out_noreboot; + spin_lock_init(&sbc8360_lock); + res = misc_register(&sbc8360_miscdev); + if (res) { + printk(KERN_ERR PFX "failed to register misc device\n"); + goto out_nomisc; } wd_margin = wd_times[timeout][0]; @@ -383,13 +383,13 @@ static int __init sbc8360_init(void) return 0; + out_nomisc: + unregister_reboot_notifier(&sbc8360_notifier); out_noreboot: - release_region(SBC8360_ENABLE, 1); release_region(SBC8360_BASETIME, 1); - out_noenablereg: out_nobasetimereg: - misc_deregister(&sbc8360_miscdev); - out_nomisc: + release_region(SBC8360_ENABLE, 1); + out: return res; } diff --git a/drivers/char/watchdog/w83627hf_wdt.c b/drivers/char/watchdog/w83627hf_wdt.c index 337ee42c90dd..b46e7f47d705 100644 --- a/drivers/char/watchdog/w83627hf_wdt.c +++ b/drivers/char/watchdog/w83627hf_wdt.c @@ -1,5 +1,8 @@ /* - * w83627hf WDT driver + * w83627hf/thf WDT driver + * + * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com> + * added support for W83627THF. * * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com> * @@ -39,7 +42,7 @@ #include <asm/uaccess.h> #include <asm/system.h> -#define WATCHDOG_NAME "w83627hf WDT" +#define WATCHDOG_NAME "w83627hf/thf WDT" #define PFX WATCHDOG_NAME ": " #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ @@ -50,7 +53,7 @@ static spinlock_t io_lock; /* You must set this - there is no sane way to probe for this board. */ static int wdt_io = 0x2E; module_param(wdt_io, int, 0); -MODULE_PARM_DESC(wdt_io, "w83627hf WDT io port (default 0x2E)"); +MODULE_PARM_DESC(wdt_io, "w83627hf/thf WDT io port (default 0x2E)"); static int timeout = WATCHDOG_TIMEOUT; /* in seconds */ module_param(timeout, int, 0); @@ -71,9 +74,19 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" _ static void w83627hf_select_wd_register(void) { + unsigned char c; outb_p(0x87, WDT_EFER); /* Enter extended function mode */ outb_p(0x87, WDT_EFER); /* Again according to manual */ + outb(0x20, WDT_EFER); /* check chip version */ + c = inb(WDT_EFDR); + if (c == 0x82) { /* W83627THF */ + outb_p(0x2b, WDT_EFER); /* select GPIO3 */ + c = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */ + outb_p(0x2b, WDT_EFER); + outb_p(c, WDT_EFDR); /* set GPIO3 to WDT0 */ + } + outb_p(0x07, WDT_EFER); /* point to logical device number reg */ outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */ outb_p(0x30, WDT_EFER); /* select CR30 */ @@ -311,7 +324,7 @@ wdt_init(void) spin_lock_init(&io_lock); - printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF Super I/O chip initialising.\n"); + printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF/THF Super I/O chip initialising.\n"); if (wdt_set_heartbeat(timeout)) { wdt_set_heartbeat(WATCHDOG_TIMEOUT); @@ -367,5 +380,5 @@ module_exit(wdt_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Pádraig Brady <P@draigBrady.com>"); -MODULE_DESCRIPTION("w83627hf WDT driver"); +MODULE_DESCRIPTION("w83627hf/thf WDT driver"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index f4c634504d1a..e678a33ea672 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -56,4 +56,26 @@ config CRYPTO_DEV_GEODE To compile this driver as a module, choose M here: the module will be called geode-aes. +config ZCRYPT + tristate "Support for PCI-attached cryptographic adapters" + depends on S390 + select ZCRYPT_MONOLITHIC if ZCRYPT="y" + default "m" + help + Select this option if you want to use a PCI-attached cryptographic + adapter like: + + PCI Cryptographic Accelerator (PCICA) + + PCI Cryptographic Coprocessor (PCICC) + + PCI-X Cryptographic Coprocessor (PCIXCC) + + Crypto Express2 Coprocessor (CEX2C) + + Crypto Express2 Accelerator (CEX2A) + +config ZCRYPT_MONOLITHIC + bool "Monolithic zcrypt module" + depends on ZCRYPT="m" + help + Select this option if you want to have a single module z90crypt.ko + that contains all parts of the crypto device driver (ap bus, + request router and all the card drivers). + endmenu diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 30d021d1a07c..72be6c63edfc 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -3,6 +3,7 @@ # menu "DMA Engine support" + depends on !S390 config DMA_ENGINE bool "Support for DMA engines" diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 4f0898400c6d..807c402df049 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -7,6 +7,7 @@ # menu 'EDAC - error detection and reporting (RAS) (EXPERIMENTAL)' + depends on HAS_IOMEM config EDAC tristate "EDAC core system error reporting (EXPERIMENTAL)" diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig new file mode 100644 index 000000000000..5932c72f9e42 --- /dev/null +++ b/drivers/firewire/Kconfig @@ -0,0 +1,61 @@ +# -*- shell-script -*- + +comment "An alternative FireWire stack is available with EXPERIMENTAL=y" + depends on EXPERIMENTAL=n + +config FIREWIRE + tristate "IEEE 1394 (FireWire) support (JUJU alternative stack, experimental)" + depends on EXPERIMENTAL + select CRC_ITU_T + help + IEEE 1394 describes a high performance serial bus, which is also + known as FireWire(tm) or i.Link(tm) and is used for connecting all + sorts of devices (most notably digital video cameras) to your + computer. + + If you have FireWire hardware and want to use it, say Y here. This + is the core support only, you will also need to select a driver for + your IEEE 1394 adapter. + + To compile this driver as a module, say M here: the module will be + called fw-core. + + This is the "JUJU" FireWire stack, an alternative implementation + designed for robustness and simplicity. You can build either this + stack, or the classic stack (the ieee1394 driver, ohci1394 etc.) + or both. + +config FIREWIRE_OHCI + tristate "Support for OHCI FireWire host controllers" + depends on PCI && FIREWIRE + help + Enable this driver if you have a FireWire controller based + on the OHCI specification. For all practical purposes, this + is the only chipset in use, so say Y here. + + To compile this driver as a module, say M here: The module will be + called fw-ohci. + + If you also build ohci1394 of the classic IEEE 1394 driver stack, + blacklist either ohci1394 or fw-ohci to let hotplug load the desired + driver. + +config FIREWIRE_SBP2 + tristate "Support for storage devices (SBP-2 protocol driver)" + depends on FIREWIRE && SCSI + help + This option enables you to use SBP-2 devices connected to a + FireWire bus. SBP-2 devices include storage devices like + harddisks and DVD drives, also some other FireWire devices + like scanners. + + To compile this driver as a module, say M here: The module will be + called fw-sbp2. + + You should also enable support for disks, CD-ROMs, etc. in the SCSI + configuration section. + + If you also build sbp2 of the classic IEEE 1394 driver stack, + blacklist either sbp2 or fw-sbp2 to let hotplug load the desired + driver. + diff --git a/drivers/firewire/Makefile b/drivers/firewire/Makefile new file mode 100644 index 000000000000..fc7d59d4bce0 --- /dev/null +++ b/drivers/firewire/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the Linux IEEE 1394 implementation +# + +fw-core-y += fw-card.o fw-topology.o fw-transaction.o fw-iso.o \ + fw-device.o fw-cdev.o + +obj-$(CONFIG_FIREWIRE) += fw-core.o +obj-$(CONFIG_FIREWIRE_OHCI) += fw-ohci.o +obj-$(CONFIG_FIREWIRE_SBP2) += fw-sbp2.o diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c new file mode 100644 index 000000000000..636151a64add --- /dev/null +++ b/drivers/firewire/fw-card.c @@ -0,0 +1,560 @@ +/* + * Copyright (C) 2005-2007 Kristian Hoegsberg <krh@bitplanet.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/crc-itu-t.h> +#include "fw-transaction.h" +#include "fw-topology.h" +#include "fw-device.h" + +int fw_compute_block_crc(u32 *block) +{ + __be32 be32_block[256]; + int i, length; + + length = (*block >> 16) & 0xff; + for (i = 0; i < length; i++) + be32_block[i] = cpu_to_be32(block[i + 1]); + *block |= crc_itu_t(0, (u8 *) be32_block, length * 4); + + return length; +} + +static DEFINE_MUTEX(card_mutex); +static LIST_HEAD(card_list); + +static LIST_HEAD(descriptor_list); +static int descriptor_count; + +#define BIB_CRC(v) ((v) << 0) +#define BIB_CRC_LENGTH(v) ((v) << 16) +#define BIB_INFO_LENGTH(v) ((v) << 24) + +#define BIB_LINK_SPEED(v) ((v) << 0) +#define BIB_GENERATION(v) ((v) << 4) +#define BIB_MAX_ROM(v) ((v) << 8) +#define BIB_MAX_RECEIVE(v) ((v) << 12) +#define BIB_CYC_CLK_ACC(v) ((v) << 16) +#define BIB_PMC ((1) << 27) +#define BIB_BMC ((1) << 28) +#define BIB_ISC ((1) << 29) +#define BIB_CMC ((1) << 30) +#define BIB_IMC ((1) << 31) + +static u32 * +generate_config_rom(struct fw_card *card, size_t *config_rom_length) +{ + struct fw_descriptor *desc; + static u32 config_rom[256]; + int i, j, length; + + /* + * Initialize contents of config rom buffer. On the OHCI + * controller, block reads to the config rom accesses the host + * memory, but quadlet read access the hardware bus info block + * registers. That's just crack, but it means we should make + * sure the contents of bus info block in host memory mathces + * the version stored in the OHCI registers. + */ + + memset(config_rom, 0, sizeof(config_rom)); + config_rom[0] = BIB_CRC_LENGTH(4) | BIB_INFO_LENGTH(4) | BIB_CRC(0); + config_rom[1] = 0x31333934; + + config_rom[2] = + BIB_LINK_SPEED(card->link_speed) | + BIB_GENERATION(card->config_rom_generation++ % 14 + 2) | + BIB_MAX_ROM(2) | + BIB_MAX_RECEIVE(card->max_receive) | + BIB_BMC | BIB_ISC | BIB_CMC | BIB_IMC; + config_rom[3] = card->guid >> 32; + config_rom[4] = card->guid; + + /* Generate root directory. */ + i = 5; + config_rom[i++] = 0; + config_rom[i++] = 0x0c0083c0; /* node capabilities */ + j = i + descriptor_count; + + /* Generate root directory entries for descriptors. */ + list_for_each_entry (desc, &descriptor_list, link) { + if (desc->immediate > 0) + config_rom[i++] = desc->immediate; + config_rom[i] = desc->key | (j - i); + i++; + j += desc->length; + } + + /* Update root directory length. */ + config_rom[5] = (i - 5 - 1) << 16; + + /* End of root directory, now copy in descriptors. */ + list_for_each_entry (desc, &descriptor_list, link) { + memcpy(&config_rom[i], desc->data, desc->length * 4); + i += desc->length; + } + + /* Calculate CRCs for all blocks in the config rom. This + * assumes that CRC length and info length are identical for + * the bus info block, which is always the case for this + * implementation. */ + for (i = 0; i < j; i += length + 1) + length = fw_compute_block_crc(config_rom + i); + + *config_rom_length = j; + + return config_rom; +} + +static void +update_config_roms(void) +{ + struct fw_card *card; + u32 *config_rom; + size_t length; + + list_for_each_entry (card, &card_list, link) { + config_rom = generate_config_rom(card, &length); + card->driver->set_config_rom(card, config_rom, length); + } +} + +int +fw_core_add_descriptor(struct fw_descriptor *desc) +{ + size_t i; + + /* + * Check descriptor is valid; the length of all blocks in the + * descriptor has to add up to exactly the length of the + * block. + */ + i = 0; + while (i < desc->length) + i += (desc->data[i] >> 16) + 1; + + if (i != desc->length) + return -EINVAL; + + mutex_lock(&card_mutex); + + list_add_tail(&desc->link, &descriptor_list); + descriptor_count++; + if (desc->immediate > 0) + descriptor_count++; + update_config_roms(); + + mutex_unlock(&card_mutex); + + return 0; +} +EXPORT_SYMBOL(fw_core_add_descriptor); + +void +fw_core_remove_descriptor(struct fw_descriptor *desc) +{ + mutex_lock(&card_mutex); + + list_del(&desc->link); + descriptor_count--; + if (desc->immediate > 0) + descriptor_count--; + update_config_roms(); + + mutex_unlock(&card_mutex); +} +EXPORT_SYMBOL(fw_core_remove_descriptor); + +static const char gap_count_table[] = { + 63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40 +}; + +struct bm_data { + struct fw_transaction t; + struct { + __be32 arg; + __be32 data; + } lock; + u32 old; + int rcode; + struct completion done; +}; + +static void +complete_bm_lock(struct fw_card *card, int rcode, + void *payload, size_t length, void *data) +{ + struct bm_data *bmd = data; + + if (rcode == RCODE_COMPLETE) + bmd->old = be32_to_cpu(*(__be32 *) payload); + bmd->rcode = rcode; + complete(&bmd->done); +} + +static void +fw_card_bm_work(struct work_struct *work) +{ + struct fw_card *card = container_of(work, struct fw_card, work.work); + struct fw_device *root; + struct bm_data bmd; + unsigned long flags; + int root_id, new_root_id, irm_id, gap_count, generation, grace; + int do_reset = 0; + + spin_lock_irqsave(&card->lock, flags); + + generation = card->generation; + root = card->root_node->data; + root_id = card->root_node->node_id; + grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10)); + + if (card->bm_generation + 1 == generation || + (card->bm_generation != generation && grace)) { + /* + * This first step is to figure out who is IRM and + * then try to become bus manager. If the IRM is not + * well defined (e.g. does not have an active link + * layer or does not responds to our lock request, we + * will have to do a little vigilante bus management. + * In that case, we do a goto into the gap count logic + * so that when we do the reset, we still optimize the + * gap count. That could well save a reset in the + * next generation. + */ + + irm_id = card->irm_node->node_id; + if (!card->irm_node->link_on) { + new_root_id = card->local_node->node_id; + fw_notify("IRM has link off, making local node (%02x) root.\n", + new_root_id); + goto pick_me; + } + + bmd.lock.arg = cpu_to_be32(0x3f); + bmd.lock.data = cpu_to_be32(card->local_node->node_id); + + spin_unlock_irqrestore(&card->lock, flags); + + init_completion(&bmd.done); + fw_send_request(card, &bmd.t, TCODE_LOCK_COMPARE_SWAP, + irm_id, generation, + SCODE_100, CSR_REGISTER_BASE + CSR_BUS_MANAGER_ID, + &bmd.lock, sizeof(bmd.lock), + complete_bm_lock, &bmd); + wait_for_completion(&bmd.done); + + if (bmd.rcode == RCODE_GENERATION) { + /* + * Another bus reset happened. Just return, + * the BM work has been rescheduled. + */ + return; + } + + if (bmd.rcode == RCODE_COMPLETE && bmd.old != 0x3f) + /* Somebody else is BM, let them do the work. */ + return; + + spin_lock_irqsave(&card->lock, flags); + if (bmd.rcode != RCODE_COMPLETE) { + /* + * The lock request failed, maybe the IRM + * isn't really IRM capable after all. Let's + * do a bus reset and pick the local node as + * root, and thus, IRM. + */ + new_root_id = card->local_node->node_id; + fw_notify("BM lock failed, making local node (%02x) root.\n", + new_root_id); + goto pick_me; + } + } else if (card->bm_generation != generation) { + /* + * OK, we weren't BM in the last generation, and it's + * less than 100ms since last bus reset. Reschedule + * this task 100ms from now. + */ + spin_unlock_irqrestore(&card->lock, flags); + schedule_delayed_work(&card->work, DIV_ROUND_UP(HZ, 10)); + return; + } + + /* + * We're bus manager for this generation, so next step is to + * make sure we have an active cycle master and do gap count + * optimization. + */ + card->bm_generation = generation; + + if (root == NULL) { + /* + * Either link_on is false, or we failed to read the + * config rom. In either case, pick another root. + */ + new_root_id = card->local_node->node_id; + } else if (atomic_read(&root->state) != FW_DEVICE_RUNNING) { + /* + * If we haven't probed this device yet, bail out now + * and let's try again once that's done. + */ + spin_unlock_irqrestore(&card->lock, flags); + return; + } else if (root->config_rom[2] & BIB_CMC) { + /* + * FIXME: I suppose we should set the cmstr bit in the + * STATE_CLEAR register of this node, as described in + * 1394-1995, 8.4.2.6. Also, send out a force root + * packet for this node. + */ + new_root_id = root_id; + } else { + /* + * Current root has an active link layer and we + * successfully read the config rom, but it's not + * cycle master capable. + */ + new_root_id = card->local_node->node_id; + } + + pick_me: + /* Now figure out what gap count to set. */ + if (card->topology_type == FW_TOPOLOGY_A && + card->root_node->max_hops < ARRAY_SIZE(gap_count_table)) + gap_count = gap_count_table[card->root_node->max_hops]; + else + gap_count = 63; + + /* + * Finally, figure out if we should do a reset or not. If we've + * done less that 5 resets with the same physical topology and we + * have either a new root or a new gap count setting, let's do it. + */ + + if (card->bm_retries++ < 5 && + (card->gap_count != gap_count || new_root_id != root_id)) + do_reset = 1; + + spin_unlock_irqrestore(&card->lock, flags); + + if (do_reset) { + fw_notify("phy config: card %d, new root=%x, gap_count=%d\n", + card->index, new_root_id, gap_count); + fw_send_phy_config(card, new_root_id, generation, gap_count); + fw_core_initiate_bus_reset(card, 1); + } +} + +static void +flush_timer_callback(unsigned long data) +{ + struct fw_card *card = (struct fw_card *)data; + + fw_flush_transactions(card); +} + +void +fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver, + struct device *device) +{ + static atomic_t index = ATOMIC_INIT(-1); + + kref_init(&card->kref); + card->index = atomic_inc_return(&index); + card->driver = driver; + card->device = device; + card->current_tlabel = 0; + card->tlabel_mask = 0; + card->color = 0; + + INIT_LIST_HEAD(&card->transaction_list); + spin_lock_init(&card->lock); + setup_timer(&card->flush_timer, + flush_timer_callback, (unsigned long)card); + + card->local_node = NULL; + + INIT_DELAYED_WORK(&card->work, fw_card_bm_work); +} +EXPORT_SYMBOL(fw_card_initialize); + +int +fw_card_add(struct fw_card *card, + u32 max_receive, u32 link_speed, u64 guid) +{ + u32 *config_rom; + size_t length; + + card->max_receive = max_receive; + card->link_speed = link_speed; + card->guid = guid; + + /* Activate link_on bit and contender bit in our self ID packets.*/ + if (card->driver->update_phy_reg(card, 4, 0, + PHY_LINK_ACTIVE | PHY_CONTENDER) < 0) + return -EIO; + + /* + * The subsystem grabs a reference when the card is added and + * drops it when the driver calls fw_core_remove_card. + */ + fw_card_get(card); + + mutex_lock(&card_mutex); + config_rom = generate_config_rom(card, &length); + list_add_tail(&card->link, &card_list); + mutex_unlock(&card_mutex); + + return card->driver->enable(card, config_rom, length); +} +EXPORT_SYMBOL(fw_card_add); + + +/* + * The next few functions implements a dummy driver that use once a + * card driver shuts down an fw_card. This allows the driver to + * cleanly unload, as all IO to the card will be handled by the dummy + * driver instead of calling into the (possibly) unloaded module. The + * dummy driver just fails all IO. + */ + +static int +dummy_enable(struct fw_card *card, u32 *config_rom, size_t length) +{ + BUG(); + return -1; +} + +static int +dummy_update_phy_reg(struct fw_card *card, int address, + int clear_bits, int set_bits) +{ + return -ENODEV; +} + +static int +dummy_set_config_rom(struct fw_card *card, + u32 *config_rom, size_t length) +{ + /* + * We take the card out of card_list before setting the dummy + * driver, so this should never get called. + */ + BUG(); + return -1; +} + +static void +dummy_send_request(struct fw_card *card, struct fw_packet *packet) +{ + packet->callback(packet, card, -ENODEV); +} + +static void +dummy_send_response(struct fw_card *card, struct fw_packet *packet) +{ + packet->callback(packet, card, -ENODEV); +} + +static int +dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet) +{ + return -ENOENT; +} + +static int +dummy_enable_phys_dma(struct fw_card *card, + int node_id, int generation) +{ + return -ENODEV; +} + +static struct fw_card_driver dummy_driver = { + .name = "dummy", + .enable = dummy_enable, + .update_phy_reg = dummy_update_phy_reg, + .set_config_rom = dummy_set_config_rom, + .send_request = dummy_send_request, + .cancel_packet = dummy_cancel_packet, + .send_response = dummy_send_response, + .enable_phys_dma = dummy_enable_phys_dma, +}; + +void +fw_core_remove_card(struct fw_card *card) +{ + card->driver->update_phy_reg(card, 4, + PHY_LINK_ACTIVE | PHY_CONTENDER, 0); + fw_core_initiate_bus_reset(card, 1); + + mutex_lock(&card_mutex); + list_del(&card->link); + mutex_unlock(&card_mutex); + + /* Set up the dummy driver. */ + card->driver = &dummy_driver; + + fw_flush_transactions(card); + + fw_destroy_nodes(card); + + fw_card_put(card); +} +EXPORT_SYMBOL(fw_core_remove_card); + +struct fw_card * +fw_card_get(struct fw_card *card) +{ + kref_get(&card->kref); + + return card; +} +EXPORT_SYMBOL(fw_card_get); + +static void +release_card(struct kref *kref) +{ + struct fw_card *card = container_of(kref, struct fw_card, kref); + + kfree(card); +} + +/* + * An assumption for fw_card_put() is that the card driver allocates + * the fw_card struct with kalloc and that it has been shut down + * before the last ref is dropped. + */ +void +fw_card_put(struct fw_card *card) +{ + kref_put(&card->kref, release_card); +} +EXPORT_SYMBOL(fw_card_put); + +int +fw_core_initiate_bus_reset(struct fw_card *card, int short_reset) +{ + int reg = short_reset ? 5 : 1; + int bit = short_reset ? PHY_BUS_SHORT_RESET : PHY_BUS_RESET; + + return card->driver->update_phy_reg(card, reg, 0, bit); +} +EXPORT_SYMBOL(fw_core_initiate_bus_reset); diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c new file mode 100644 index 000000000000..0fa5bd54c6a1 --- /dev/null +++ b/drivers/firewire/fw-cdev.c @@ -0,0 +1,961 @@ +/* + * Char device for device raw access + * + * Copyright (C) 2005-2007 Kristian Hoegsberg <krh@bitplanet.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/vmalloc.h> +#include <linux/poll.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/idr.h> +#include <linux/compat.h> +#include <linux/firewire-cdev.h> +#include <asm/uaccess.h> +#include "fw-transaction.h" +#include "fw-topology.h" +#include "fw-device.h" + +struct client; +struct client_resource { + struct list_head link; + void (*release)(struct client *client, struct client_resource *r); + u32 handle; +}; + +/* + * dequeue_event() just kfree()'s the event, so the event has to be + * the first field in the struct. + */ + +struct event { + struct { void *data; size_t size; } v[2]; + struct list_head link; +}; + +struct bus_reset { + struct event event; + struct fw_cdev_event_bus_reset reset; +}; + +struct response { + struct event event; + struct fw_transaction transaction; + struct client *client; + struct client_resource resource; + struct fw_cdev_event_response response; +}; + +struct iso_interrupt { + struct event event; + struct fw_cdev_event_iso_interrupt interrupt; +}; + +struct client { + u32 version; + struct fw_device *device; + spinlock_t lock; + u32 resource_handle; + struct list_head resource_list; + struct list_head event_list; + wait_queue_head_t wait; + u64 bus_reset_closure; + + struct fw_iso_context *iso_context; + u64 iso_closure; + struct fw_iso_buffer buffer; + unsigned long vm_start; + + struct list_head link; +}; + +static inline void __user * +u64_to_uptr(__u64 value) +{ + return (void __user *)(unsigned long)value; +} + +static inline __u64 +uptr_to_u64(void __user *ptr) +{ + return (__u64)(unsigned long)ptr; +} + +static int fw_device_op_open(struct inode *inode, struct file *file) +{ + struct fw_device *device; + struct client *client; + unsigned long flags; + + device = fw_device_from_devt(inode->i_rdev); + if (device == NULL) + return -ENODEV; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (client == NULL) + return -ENOMEM; + + client->device = fw_device_get(device); + INIT_LIST_HEAD(&client->event_list); + INIT_LIST_HEAD(&client->resource_list); + spin_lock_init(&client->lock); + init_waitqueue_head(&client->wait); + + file->private_data = client; + + spin_lock_irqsave(&device->card->lock, flags); + list_add_tail(&client->link, &device->client_list); + spin_unlock_irqrestore(&device->card->lock, flags); + + return 0; +} + +static void queue_event(struct client *client, struct event *event, + void *data0, size_t size0, void *data1, size_t size1) +{ + unsigned long flags; + + event->v[0].data = data0; + event->v[0].size = size0; + event->v[1].data = data1; + event->v[1].size = size1; + + spin_lock_irqsave(&client->lock, flags); + + list_add_tail(&event->link, &client->event_list); + wake_up_interruptible(&client->wait); + + spin_unlock_irqrestore(&client->lock, flags); +} + +static int +dequeue_event(struct client *client, char __user *buffer, size_t count) +{ + unsigned long flags; + struct event *event; + size_t size, total; + int i, retval; + + retval = wait_event_interruptible(client->wait, + !list_empty(&client->event_list) || + fw_device_is_shutdown(client->device)); + if (retval < 0) + return retval; + + if (list_empty(&client->event_list) && + fw_device_is_shutdown(client->device)) + return -ENODEV; + + spin_lock_irqsave(&client->lock, flags); + event = container_of(client->event_list.next, struct event, link); + list_del(&event->link); + spin_unlock_irqrestore(&client->lock, flags); + + total = 0; + for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) { + size = min(event->v[i].size, count - total); + if (copy_to_user(buffer + total, event->v[i].data, size)) { + retval = -EFAULT; + goto out; + } + total += size; + } + retval = total; + + out: + kfree(event); + + return retval; +} + +static ssize_t +fw_device_op_read(struct file *file, + char __user *buffer, size_t count, loff_t *offset) +{ + struct client *client = file->private_data; + + return dequeue_event(client, buffer, count); +} + +static void +fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, + struct client *client) +{ + struct fw_card *card = client->device->card; + + event->closure = client->bus_reset_closure; + event->type = FW_CDEV_EVENT_BUS_RESET; + event->node_id = client->device->node_id; + event->local_node_id = card->local_node->node_id; + event->bm_node_id = 0; /* FIXME: We don't track the BM. */ + event->irm_node_id = card->irm_node->node_id; + event->root_node_id = card->root_node->node_id; + event->generation = card->generation; +} + +static void +for_each_client(struct fw_device *device, + void (*callback)(struct client *client)) +{ + struct fw_card *card = device->card; + struct client *c; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + list_for_each_entry(c, &device->client_list, link) + callback(c); + + spin_unlock_irqrestore(&card->lock, flags); +} + +static void +queue_bus_reset_event(struct client *client) +{ + struct bus_reset *bus_reset; + + bus_reset = kzalloc(sizeof(*bus_reset), GFP_ATOMIC); + if (bus_reset == NULL) { + fw_notify("Out of memory when allocating bus reset event\n"); + return; + } + + fill_bus_reset_event(&bus_reset->reset, client); + + queue_event(client, &bus_reset->event, + &bus_reset->reset, sizeof(bus_reset->reset), NULL, 0); +} + +void fw_device_cdev_update(struct fw_device *device) +{ + for_each_client(device, queue_bus_reset_event); +} + +static void wake_up_client(struct client *client) +{ + wake_up_interruptible(&client->wait); +} + +void fw_device_cdev_remove(struct fw_device *device) +{ + for_each_client(device, wake_up_client); +} + +static int ioctl_get_info(struct client *client, void *buffer) +{ + struct fw_cdev_get_info *get_info = buffer; + struct fw_cdev_event_bus_reset bus_reset; + + client->version = get_info->version; + get_info->version = FW_CDEV_VERSION; + + if (get_info->rom != 0) { + void __user *uptr = u64_to_uptr(get_info->rom); + size_t want = get_info->rom_length; + size_t have = client->device->config_rom_length * 4; + + if (copy_to_user(uptr, client->device->config_rom, + min(want, have))) + return -EFAULT; + } + get_info->rom_length = client->device->config_rom_length * 4; + + client->bus_reset_closure = get_info->bus_reset_closure; + if (get_info->bus_reset != 0) { + void __user *uptr = u64_to_uptr(get_info->bus_reset); + + fill_bus_reset_event(&bus_reset, client); + if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset))) + return -EFAULT; + } + + get_info->card = client->device->card->index; + + return 0; +} + +static void +add_client_resource(struct client *client, struct client_resource *resource) +{ + unsigned long flags; + + spin_lock_irqsave(&client->lock, flags); + list_add_tail(&resource->link, &client->resource_list); + resource->handle = client->resource_handle++; + spin_unlock_irqrestore(&client->lock, flags); +} + +static int +release_client_resource(struct client *client, u32 handle, + struct client_resource **resource) +{ + struct client_resource *r; + unsigned long flags; + + spin_lock_irqsave(&client->lock, flags); + list_for_each_entry(r, &client->resource_list, link) { + if (r->handle == handle) { + list_del(&r->link); + break; + } + } + spin_unlock_irqrestore(&client->lock, flags); + + if (&r->link == &client->resource_list) + return -EINVAL; + + if (resource) + *resource = r; + else + r->release(client, r); + + return 0; +} + +static void +release_transaction(struct client *client, struct client_resource *resource) +{ + struct response *response = + container_of(resource, struct response, resource); + + fw_cancel_transaction(client->device->card, &response->transaction); +} + +static void +complete_transaction(struct fw_card *card, int rcode, + void *payload, size_t length, void *data) +{ + struct response *response = data; + struct client *client = response->client; + unsigned long flags; + + if (length < response->response.length) + response->response.length = length; + if (rcode == RCODE_COMPLETE) + memcpy(response->response.data, payload, + response->response.length); + + spin_lock_irqsave(&client->lock, flags); + list_del(&response->resource.link); + spin_unlock_irqrestore(&client->lock, flags); + + response->response.type = FW_CDEV_EVENT_RESPONSE; + response->response.rcode = rcode; + queue_event(client, &response->event, + &response->response, sizeof(response->response), + response->response.data, response->response.length); +} + +static ssize_t ioctl_send_request(struct client *client, void *buffer) +{ + struct fw_device *device = client->device; + struct fw_cdev_send_request *request = buffer; + struct response *response; + + /* What is the biggest size we'll accept, really? */ + if (request->length > 4096) + return -EINVAL; + + response = kmalloc(sizeof(*response) + request->length, GFP_KERNEL); + if (response == NULL) + return -ENOMEM; + + response->client = client; + response->response.length = request->length; + response->response.closure = request->closure; + + if (request->data && + copy_from_user(response->response.data, + u64_to_uptr(request->data), request->length)) { + kfree(response); + return -EFAULT; + } + + response->resource.release = release_transaction; + add_client_resource(client, &response->resource); + + fw_send_request(device->card, &response->transaction, + request->tcode & 0x1f, + device->node->node_id, + request->generation, + device->node->max_speed, + request->offset, + response->response.data, request->length, + complete_transaction, response); + + if (request->data) + return sizeof(request) + request->length; + else + return sizeof(request); +} + +struct address_handler { + struct fw_address_handler handler; + __u64 closure; + struct client *client; + struct client_resource resource; +}; + +struct request { + struct fw_request *request; + void *data; + size_t length; + struct client_resource resource; +}; + +struct request_event { + struct event event; + struct fw_cdev_event_request request; +}; + +static void +release_request(struct client *client, struct client_resource *resource) +{ + struct request *request = + container_of(resource, struct request, resource); + + fw_send_response(client->device->card, request->request, + RCODE_CONFLICT_ERROR); + kfree(request); +} + +static void +handle_request(struct fw_card *card, struct fw_request *r, + int tcode, int destination, int source, + int generation, int speed, + unsigned long long offset, + void *payload, size_t length, void *callback_data) +{ + struct address_handler *handler = callback_data; + struct request *request; + struct request_event *e; + struct client *client = handler->client; + + request = kmalloc(sizeof(*request), GFP_ATOMIC); + e = kmalloc(sizeof(*e), GFP_ATOMIC); + if (request == NULL || e == NULL) { + kfree(request); + kfree(e); + fw_send_response(card, r, RCODE_CONFLICT_ERROR); + return; + } + + request->request = r; + request->data = payload; + request->length = length; + + request->resource.release = release_request; + add_client_resource(client, &request->resource); + + e->request.type = FW_CDEV_EVENT_REQUEST; + e->request.tcode = tcode; + e->request.offset = offset; + e->request.length = length; + e->request.handle = request->resource.handle; + e->request.closure = handler->closure; + + queue_event(client, &e->event, + &e->request, sizeof(e->request), payload, length); +} + +static void +release_address_handler(struct client *client, + struct client_resource *resource) +{ + struct address_handler *handler = + container_of(resource, struct address_handler, resource); + + fw_core_remove_address_handler(&handler->handler); + kfree(handler); +} + +static int ioctl_allocate(struct client *client, void *buffer) +{ + struct fw_cdev_allocate *request = buffer; + struct address_handler *handler; + struct fw_address_region region; + + handler = kmalloc(sizeof(*handler), GFP_KERNEL); + if (handler == NULL) + return -ENOMEM; + + region.start = request->offset; + region.end = request->offset + request->length; + handler->handler.length = request->length; + handler->handler.address_callback = handle_request; + handler->handler.callback_data = handler; + handler->closure = request->closure; + handler->client = client; + + if (fw_core_add_address_handler(&handler->handler, ®ion) < 0) { + kfree(handler); + return -EBUSY; + } + + handler->resource.release = release_address_handler; + add_client_resource(client, &handler->resource); + request->handle = handler->resource.handle; + + return 0; +} + +static int ioctl_deallocate(struct client *client, void *buffer) +{ + struct fw_cdev_deallocate *request = buffer; + + return release_client_resource(client, request->handle, NULL); +} + +static int ioctl_send_response(struct client *client, void *buffer) +{ + struct fw_cdev_send_response *request = buffer; + struct client_resource *resource; + struct request *r; + + if (release_client_resource(client, request->handle, &resource) < 0) + return -EINVAL; + r = container_of(resource, struct request, resource); + if (request->length < r->length) + r->length = request->length; + if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) + return -EFAULT; + + fw_send_response(client->device->card, r->request, request->rcode); + kfree(r); + + return 0; +} + +static int ioctl_initiate_bus_reset(struct client *client, void *buffer) +{ + struct fw_cdev_initiate_bus_reset *request = buffer; + int short_reset; + + short_reset = (request->type == FW_CDEV_SHORT_RESET); + + return fw_core_initiate_bus_reset(client->device->card, short_reset); +} + +struct descriptor { + struct fw_descriptor d; + struct client_resource resource; + u32 data[0]; +}; + +static void release_descriptor(struct client *client, + struct client_resource *resource) +{ + struct descriptor *descriptor = + container_of(resource, struct descriptor, resource); + + fw_core_remove_descriptor(&descriptor->d); + kfree(descriptor); +} + +static int ioctl_add_descriptor(struct client *client, void *buffer) +{ + struct fw_cdev_add_descriptor *request = buffer; + struct descriptor *descriptor; + int retval; + + if (request->length > 256) + return -EINVAL; + + descriptor = + kmalloc(sizeof(*descriptor) + request->length * 4, GFP_KERNEL); + if (descriptor == NULL) + return -ENOMEM; + + if (copy_from_user(descriptor->data, + u64_to_uptr(request->data), request->length * 4)) { + kfree(descriptor); + return -EFAULT; + } + + descriptor->d.length = request->length; + descriptor->d.immediate = request->immediate; + descriptor->d.key = request->key; + descriptor->d.data = descriptor->data; + + retval = fw_core_add_descriptor(&descriptor->d); + if (retval < 0) { + kfree(descriptor); + return retval; + } + + descriptor->resource.release = release_descriptor; + add_client_resource(client, &descriptor->resource); + request->handle = descriptor->resource.handle; + + return 0; +} + +static int ioctl_remove_descriptor(struct client *client, void *buffer) +{ + struct fw_cdev_remove_descriptor *request = buffer; + + return release_client_resource(client, request->handle, NULL); +} + +static void +iso_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, void *data) +{ + struct client *client = data; + struct iso_interrupt *interrupt; + + interrupt = kzalloc(sizeof(*interrupt) + header_length, GFP_ATOMIC); + if (interrupt == NULL) + return; + + interrupt->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; + interrupt->interrupt.closure = client->iso_closure; + interrupt->interrupt.cycle = cycle; + interrupt->interrupt.header_length = header_length; + memcpy(interrupt->interrupt.header, header, header_length); + queue_event(client, &interrupt->event, + &interrupt->interrupt, + sizeof(interrupt->interrupt) + header_length, NULL, 0); +} + +static int ioctl_create_iso_context(struct client *client, void *buffer) +{ + struct fw_cdev_create_iso_context *request = buffer; + + if (request->channel > 63) + return -EINVAL; + + switch (request->type) { + case FW_ISO_CONTEXT_RECEIVE: + if (request->header_size < 4 || (request->header_size & 3)) + return -EINVAL; + + break; + + case FW_ISO_CONTEXT_TRANSMIT: + if (request->speed > SCODE_3200) + return -EINVAL; + + break; + + default: + return -EINVAL; + } + + client->iso_closure = request->closure; + client->iso_context = fw_iso_context_create(client->device->card, + request->type, + request->channel, + request->speed, + request->header_size, + iso_callback, client); + if (IS_ERR(client->iso_context)) + return PTR_ERR(client->iso_context); + + /* We only support one context at this time. */ + request->handle = 0; + + return 0; +} + +static int ioctl_queue_iso(struct client *client, void *buffer) +{ + struct fw_cdev_queue_iso *request = buffer; + struct fw_cdev_iso_packet __user *p, *end, *next; + struct fw_iso_context *ctx = client->iso_context; + unsigned long payload, buffer_end, header_length; + int count; + struct { + struct fw_iso_packet packet; + u8 header[256]; + } u; + + if (ctx == NULL || request->handle != 0) + return -EINVAL; + + /* + * If the user passes a non-NULL data pointer, has mmap()'ed + * the iso buffer, and the pointer points inside the buffer, + * we setup the payload pointers accordingly. Otherwise we + * set them both to 0, which will still let packets with + * payload_length == 0 through. In other words, if no packets + * use the indirect payload, the iso buffer need not be mapped + * and the request->data pointer is ignored. + */ + + payload = (unsigned long)request->data - client->vm_start; + buffer_end = client->buffer.page_count << PAGE_SHIFT; + if (request->data == 0 || client->buffer.pages == NULL || + payload >= buffer_end) { + payload = 0; + buffer_end = 0; + } + + if (!access_ok(VERIFY_READ, request->packets, request->size)) + return -EFAULT; + + p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request->packets); + end = (void __user *)p + request->size; + count = 0; + while (p < end) { + if (__copy_from_user(&u.packet, p, sizeof(*p))) + return -EFAULT; + + if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) { + header_length = u.packet.header_length; + } else { + /* + * We require that header_length is a multiple of + * the fixed header size, ctx->header_size. + */ + if (ctx->header_size == 0) { + if (u.packet.header_length > 0) + return -EINVAL; + } else if (u.packet.header_length % ctx->header_size != 0) { + return -EINVAL; + } + header_length = 0; + } + + next = (struct fw_cdev_iso_packet __user *) + &p->header[header_length / 4]; + if (next > end) + return -EINVAL; + if (__copy_from_user + (u.packet.header, p->header, header_length)) + return -EFAULT; + if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT && + u.packet.header_length + u.packet.payload_length > 0) + return -EINVAL; + if (payload + u.packet.payload_length > buffer_end) + return -EINVAL; + + if (fw_iso_context_queue(ctx, &u.packet, + &client->buffer, payload)) + break; + + p = next; + payload += u.packet.payload_length; + count++; + } + + request->size -= uptr_to_u64(p) - request->packets; + request->packets = uptr_to_u64(p); + request->data = client->vm_start + payload; + + return count; +} + +static int ioctl_start_iso(struct client *client, void *buffer) +{ + struct fw_cdev_start_iso *request = buffer; + + if (request->handle != 0) + return -EINVAL; + if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) { + if (request->tags == 0 || request->tags > 15) + return -EINVAL; + + if (request->sync > 15) + return -EINVAL; + } + + return fw_iso_context_start(client->iso_context, request->cycle, + request->sync, request->tags); +} + +static int ioctl_stop_iso(struct client *client, void *buffer) +{ + struct fw_cdev_stop_iso *request = buffer; + + if (request->handle != 0) + return -EINVAL; + + return fw_iso_context_stop(client->iso_context); +} + +static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { + ioctl_get_info, + ioctl_send_request, + ioctl_allocate, + ioctl_deallocate, + ioctl_send_response, + ioctl_initiate_bus_reset, + ioctl_add_descriptor, + ioctl_remove_descriptor, + ioctl_create_iso_context, + ioctl_queue_iso, + ioctl_start_iso, + ioctl_stop_iso, +}; + +static int +dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg) +{ + char buffer[256]; + int retval; + + if (_IOC_TYPE(cmd) != '#' || + _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers)) + return -EINVAL; + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + if (_IOC_SIZE(cmd) > sizeof(buffer) || + copy_from_user(buffer, arg, _IOC_SIZE(cmd))) + return -EFAULT; + } + + retval = ioctl_handlers[_IOC_NR(cmd)](client, buffer); + if (retval < 0) + return retval; + + if (_IOC_DIR(cmd) & _IOC_READ) { + if (_IOC_SIZE(cmd) > sizeof(buffer) || + copy_to_user(arg, buffer, _IOC_SIZE(cmd))) + return -EFAULT; + } + + return 0; +} + +static long +fw_device_op_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct client *client = file->private_data; + + return dispatch_ioctl(client, cmd, (void __user *) arg); +} + +#ifdef CONFIG_COMPAT +static long +fw_device_op_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct client *client = file->private_data; + + return dispatch_ioctl(client, cmd, compat_ptr(arg)); +} +#endif + +static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct client *client = file->private_data; + enum dma_data_direction direction; + unsigned long size; + int page_count, retval; + + /* FIXME: We could support multiple buffers, but we don't. */ + if (client->buffer.pages != NULL) + return -EBUSY; + + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + if (vma->vm_start & ~PAGE_MASK) + return -EINVAL; + + client->vm_start = vma->vm_start; + size = vma->vm_end - vma->vm_start; + page_count = size >> PAGE_SHIFT; + if (size & ~PAGE_MASK) + return -EINVAL; + + if (vma->vm_flags & VM_WRITE) + direction = DMA_TO_DEVICE; + else + direction = DMA_FROM_DEVICE; + + retval = fw_iso_buffer_init(&client->buffer, client->device->card, + page_count, direction); + if (retval < 0) + return retval; + + retval = fw_iso_buffer_map(&client->buffer, vma); + if (retval < 0) + fw_iso_buffer_destroy(&client->buffer, client->device->card); + + return retval; +} + +static int fw_device_op_release(struct inode *inode, struct file *file) +{ + struct client *client = file->private_data; + struct event *e, *next_e; + struct client_resource *r, *next_r; + unsigned long flags; + + if (client->buffer.pages) + fw_iso_buffer_destroy(&client->buffer, client->device->card); + + if (client->iso_context) + fw_iso_context_destroy(client->iso_context); + + list_for_each_entry_safe(r, next_r, &client->resource_list, link) + r->release(client, r); + + /* + * FIXME: We should wait for the async tasklets to stop + * running before freeing the memory. + */ + + list_for_each_entry_safe(e, next_e, &client->event_list, link) + kfree(e); + + spin_lock_irqsave(&client->device->card->lock, flags); + list_del(&client->link); + spin_unlock_irqrestore(&client->device->card->lock, flags); + + fw_device_put(client->device); + kfree(client); + + return 0; +} + +static unsigned int fw_device_op_poll(struct file *file, poll_table * pt) +{ + struct client *client = file->private_data; + unsigned int mask = 0; + + poll_wait(file, &client->wait, pt); + + if (fw_device_is_shutdown(client->device)) + mask |= POLLHUP | POLLERR; + if (!list_empty(&client->event_list)) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +const struct file_operations fw_device_ops = { + .owner = THIS_MODULE, + .open = fw_device_op_open, + .read = fw_device_op_read, + .unlocked_ioctl = fw_device_op_ioctl, + .poll = fw_device_op_poll, + .release = fw_device_op_release, + .mmap = fw_device_op_mmap, + +#ifdef CONFIG_COMPAT + .compat_ioctl = fw_device_op_compat_ioctl, +#endif +}; diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c new file mode 100644 index 000000000000..c1ce465d9710 --- /dev/null +++ b/drivers/firewire/fw-device.c @@ -0,0 +1,813 @@ +/* + * Device probing and sysfs code. + * + * Copyright (C) 2005-2006 Kristian Hoegsberg <krh@bitplanet.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/wait.h> +#include <linux/errno.h> +#include <linux/kthread.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/idr.h> +#include <linux/rwsem.h> +#include <asm/semaphore.h> +#include <linux/ctype.h> +#include "fw-transaction.h" +#include "fw-topology.h" +#include "fw-device.h" + +void fw_csr_iterator_init(struct fw_csr_iterator *ci, u32 * p) +{ + ci->p = p + 1; + ci->end = ci->p + (p[0] >> 16); +} +EXPORT_SYMBOL(fw_csr_iterator_init); + +int fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value) +{ + *key = *ci->p >> 24; + *value = *ci->p & 0xffffff; + + return ci->p++ < ci->end; +} +EXPORT_SYMBOL(fw_csr_iterator_next); + +static int is_fw_unit(struct device *dev); + +static int match_unit_directory(u32 * directory, const struct fw_device_id *id) +{ + struct fw_csr_iterator ci; + int key, value, match; + + match = 0; + fw_csr_iterator_init(&ci, directory); + while (fw_csr_iterator_next(&ci, &key, &value)) { + if (key == CSR_VENDOR && value == id->vendor) + match |= FW_MATCH_VENDOR; + if (key == CSR_MODEL && value == id->model) + match |= FW_MATCH_MODEL; + if (key == CSR_SPECIFIER_ID && value == id->specifier_id) + match |= FW_MATCH_SPECIFIER_ID; + if (key == CSR_VERSION && value == id->version) + match |= FW_MATCH_VERSION; + } + + return (match & id->match_flags) == id->match_flags; +} + +static int fw_unit_match(struct device *dev, struct device_driver *drv) +{ + struct fw_unit *unit = fw_unit(dev); + struct fw_driver *driver = fw_driver(drv); + int i; + + /* We only allow binding to fw_units. */ + if (!is_fw_unit(dev)) + return 0; + + for (i = 0; driver->id_table[i].match_flags != 0; i++) { + if (match_unit_directory(unit->directory, &driver->id_table[i])) + return 1; + } + + return 0; +} + +static int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size) +{ + struct fw_device *device = fw_device(unit->device.parent); + struct fw_csr_iterator ci; + + int key, value; + int vendor = 0; + int model = 0; + int specifier_id = 0; + int version = 0; + + fw_csr_iterator_init(&ci, &device->config_rom[5]); + while (fw_csr_iterator_next(&ci, &key, &value)) { + switch (key) { + case CSR_VENDOR: + vendor = value; + break; + case CSR_MODEL: + model = value; + break; + } + } + + fw_csr_iterator_init(&ci, unit->directory); + while (fw_csr_iterator_next(&ci, &key, &value)) { + switch (key) { + case CSR_SPECIFIER_ID: + specifier_id = value; + break; + case CSR_VERSION: + version = value; + break; + } + } + + return snprintf(buffer, buffer_size, + "ieee1394:ven%08Xmo%08Xsp%08Xver%08X", + vendor, model, specifier_id, version); +} + +static int +fw_unit_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct fw_unit *unit = fw_unit(dev); + char modalias[64]; + int length = 0; + int i = 0; + + get_modalias(unit, modalias, sizeof(modalias)); + + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=%s", modalias)) + return -ENOMEM; + + envp[i] = NULL; + + return 0; +} + +struct bus_type fw_bus_type = { + .name = "firewire", + .match = fw_unit_match, +}; +EXPORT_SYMBOL(fw_bus_type); + +struct fw_device *fw_device_get(struct fw_device *device) +{ + get_device(&device->device); + + return device; +} + +void fw_device_put(struct fw_device *device) +{ + put_device(&device->device); +} + +static void fw_device_release(struct device *dev) +{ + struct fw_device *device = fw_device(dev); + unsigned long flags; + + /* + * Take the card lock so we don't set this to NULL while a + * FW_NODE_UPDATED callback is being handled. + */ + spin_lock_irqsave(&device->card->lock, flags); + device->node->data = NULL; + spin_unlock_irqrestore(&device->card->lock, flags); + + fw_node_put(device->node); + fw_card_put(device->card); + kfree(device->config_rom); + kfree(device); +} + +int fw_device_enable_phys_dma(struct fw_device *device) +{ + return device->card->driver->enable_phys_dma(device->card, + device->node_id, + device->generation); +} +EXPORT_SYMBOL(fw_device_enable_phys_dma); + +struct config_rom_attribute { + struct device_attribute attr; + u32 key; +}; + +static ssize_t +show_immediate(struct device *dev, struct device_attribute *dattr, char *buf) +{ + struct config_rom_attribute *attr = + container_of(dattr, struct config_rom_attribute, attr); + struct fw_csr_iterator ci; + u32 *dir; + int key, value; + + if (is_fw_unit(dev)) + dir = fw_unit(dev)->directory; + else + dir = fw_device(dev)->config_rom + 5; + + fw_csr_iterator_init(&ci, dir); + while (fw_csr_iterator_next(&ci, &key, &value)) + if (attr->key == key) + return snprintf(buf, buf ? PAGE_SIZE : 0, + "0x%06x\n", value); + + return -ENOENT; +} + +#define IMMEDIATE_ATTR(name, key) \ + { __ATTR(name, S_IRUGO, show_immediate, NULL), key } + +static ssize_t +show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf) +{ + struct config_rom_attribute *attr = + container_of(dattr, struct config_rom_attribute, attr); + struct fw_csr_iterator ci; + u32 *dir, *block = NULL, *p, *end; + int length, key, value, last_key = 0; + char *b; + + if (is_fw_unit(dev)) + dir = fw_unit(dev)->directory; + else + dir = fw_device(dev)->config_rom + 5; + + fw_csr_iterator_init(&ci, dir); + while (fw_csr_iterator_next(&ci, &key, &value)) { + if (attr->key == last_key && + key == (CSR_DESCRIPTOR | CSR_LEAF)) + block = ci.p - 1 + value; + last_key = key; + } + + if (block == NULL) + return -ENOENT; + + length = min(block[0] >> 16, 256U); + if (length < 3) + return -ENOENT; + + if (block[1] != 0 || block[2] != 0) + /* Unknown encoding. */ + return -ENOENT; + + if (buf == NULL) + return length * 4; + + b = buf; + end = &block[length + 1]; + for (p = &block[3]; p < end; p++, b += 4) + * (u32 *) b = (__force u32) __cpu_to_be32(*p); + + /* Strip trailing whitespace and add newline. */ + while (b--, (isspace(*b) || *b == '\0') && b > buf); + strcpy(b + 1, "\n"); + + return b + 2 - buf; +} + +#define TEXT_LEAF_ATTR(name, key) \ + { __ATTR(name, S_IRUGO, show_text_leaf, NULL), key } + +static struct config_rom_attribute config_rom_attributes[] = { + IMMEDIATE_ATTR(vendor, CSR_VENDOR), + IMMEDIATE_ATTR(hardware_version, CSR_HARDWARE_VERSION), + IMMEDIATE_ATTR(specifier_id, CSR_SPECIFIER_ID), + IMMEDIATE_ATTR(version, CSR_VERSION), + IMMEDIATE_ATTR(model, CSR_MODEL), + TEXT_LEAF_ATTR(vendor_name, CSR_VENDOR), + TEXT_LEAF_ATTR(model_name, CSR_MODEL), + TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION), +}; + +static void +init_fw_attribute_group(struct device *dev, + struct device_attribute *attrs, + struct fw_attribute_group *group) +{ + struct device_attribute *attr; + int i, j; + + for (j = 0; attrs[j].attr.name != NULL; j++) + group->attrs[j] = &attrs[j].attr; + + for (i = 0; i < ARRAY_SIZE(config_rom_attributes); i++) { + attr = &config_rom_attributes[i].attr; + if (attr->show(dev, attr, NULL) < 0) + continue; + group->attrs[j++] = &attr->attr; + } + + BUG_ON(j >= ARRAY_SIZE(group->attrs)); + group->attrs[j++] = NULL; + group->groups[0] = &group->group; + group->groups[1] = NULL; + group->group.attrs = group->attrs; + dev->groups = group->groups; +} + +static ssize_t +modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fw_unit *unit = fw_unit(dev); + int length; + + length = get_modalias(unit, buf, PAGE_SIZE); + strcpy(buf + length, "\n"); + + return length + 1; +} + +static ssize_t +rom_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fw_device *device = fw_device(dev->parent); + struct fw_unit *unit = fw_unit(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", + (int)(unit->directory - device->config_rom)); +} + +static struct device_attribute fw_unit_attributes[] = { + __ATTR_RO(modalias), + __ATTR_RO(rom_index), + __ATTR_NULL, +}; + +static ssize_t +config_rom_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fw_device *device = fw_device(dev); + + memcpy(buf, device->config_rom, device->config_rom_length * 4); + + return device->config_rom_length * 4; +} + +static ssize_t +guid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fw_device *device = fw_device(dev); + u64 guid; + + guid = ((u64)device->config_rom[3] << 32) | device->config_rom[4]; + + return snprintf(buf, PAGE_SIZE, "0x%016llx\n", + (unsigned long long)guid); +} + +static struct device_attribute fw_device_attributes[] = { + __ATTR_RO(config_rom), + __ATTR_RO(guid), + __ATTR_NULL, +}; + +struct read_quadlet_callback_data { + struct completion done; + int rcode; + u32 data; +}; + +static void +complete_transaction(struct fw_card *card, int rcode, + void *payload, size_t length, void *data) +{ + struct read_quadlet_callback_data *callback_data = data; + + if (rcode == RCODE_COMPLETE) + callback_data->data = be32_to_cpu(*(__be32 *)payload); + callback_data->rcode = rcode; + complete(&callback_data->done); +} + +static int read_rom(struct fw_device *device, int index, u32 * data) +{ + struct read_quadlet_callback_data callback_data; + struct fw_transaction t; + u64 offset; + + init_completion(&callback_data.done); + + offset = 0xfffff0000400ULL + index * 4; + fw_send_request(device->card, &t, TCODE_READ_QUADLET_REQUEST, + device->node_id, + device->generation, SCODE_100, + offset, NULL, 4, complete_transaction, &callback_data); + + wait_for_completion(&callback_data.done); + + *data = callback_data.data; + + return callback_data.rcode; +} + +static int read_bus_info_block(struct fw_device *device) +{ + static u32 rom[256]; + u32 stack[16], sp, key; + int i, end, length; + + /* First read the bus info block. */ + for (i = 0; i < 5; i++) { + if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE) + return -1; + /* + * As per IEEE1212 7.2, during power-up, devices can + * reply with a 0 for the first quadlet of the config + * rom to indicate that they are booting (for example, + * if the firmware is on the disk of a external + * harddisk). In that case we just fail, and the + * retry mechanism will try again later. + */ + if (i == 0 && rom[i] == 0) + return -1; + } + + /* + * Now parse the config rom. The config rom is a recursive + * directory structure so we parse it using a stack of + * references to the blocks that make up the structure. We + * push a reference to the root directory on the stack to + * start things off. + */ + length = i; + sp = 0; + stack[sp++] = 0xc0000005; + while (sp > 0) { + /* + * Pop the next block reference of the stack. The + * lower 24 bits is the offset into the config rom, + * the upper 8 bits are the type of the reference the + * block. + */ + key = stack[--sp]; + i = key & 0xffffff; + if (i >= ARRAY_SIZE(rom)) + /* + * The reference points outside the standard + * config rom area, something's fishy. + */ + return -1; + + /* Read header quadlet for the block to get the length. */ + if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE) + return -1; + end = i + (rom[i] >> 16) + 1; + i++; + if (end > ARRAY_SIZE(rom)) + /* + * This block extends outside standard config + * area (and the array we're reading it + * into). That's broken, so ignore this + * device. + */ + return -1; + + /* + * Now read in the block. If this is a directory + * block, check the entries as we read them to see if + * it references another block, and push it in that case. + */ + while (i < end) { + if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE) + return -1; + if ((key >> 30) == 3 && (rom[i] >> 30) > 1 && + sp < ARRAY_SIZE(stack)) + stack[sp++] = i + rom[i]; + i++; + } + if (length < i) + length = i; + } + + device->config_rom = kmalloc(length * 4, GFP_KERNEL); + if (device->config_rom == NULL) + return -1; + memcpy(device->config_rom, rom, length * 4); + device->config_rom_length = length; + + return 0; +} + +static void fw_unit_release(struct device *dev) +{ + struct fw_unit *unit = fw_unit(dev); + + kfree(unit); +} + +static struct device_type fw_unit_type = { + .uevent = fw_unit_uevent, + .release = fw_unit_release, +}; + +static int is_fw_unit(struct device *dev) +{ + return dev->type == &fw_unit_type; +} + +static void create_units(struct fw_device *device) +{ + struct fw_csr_iterator ci; + struct fw_unit *unit; + int key, value, i; + + i = 0; + fw_csr_iterator_init(&ci, &device->config_rom[5]); + while (fw_csr_iterator_next(&ci, &key, &value)) { + if (key != (CSR_UNIT | CSR_DIRECTORY)) + continue; + + /* + * Get the address of the unit directory and try to + * match the drivers id_tables against it. + */ + unit = kzalloc(sizeof(*unit), GFP_KERNEL); + if (unit == NULL) { + fw_error("failed to allocate memory for unit\n"); + continue; + } + + unit->directory = ci.p + value - 1; + unit->device.bus = &fw_bus_type; + unit->device.type = &fw_unit_type; + unit->device.parent = &device->device; + snprintf(unit->device.bus_id, sizeof(unit->device.bus_id), + "%s.%d", device->device.bus_id, i++); + + init_fw_attribute_group(&unit->device, + fw_unit_attributes, + &unit->attribute_group); + if (device_register(&unit->device) < 0) + goto skip_unit; + + continue; + + skip_unit: + kfree(unit); + } +} + +static int shutdown_unit(struct device *device, void *data) +{ + device_unregister(device); + + return 0; +} + +static DECLARE_RWSEM(idr_rwsem); +static DEFINE_IDR(fw_device_idr); +int fw_cdev_major; + +struct fw_device *fw_device_from_devt(dev_t devt) +{ + struct fw_device *device; + + down_read(&idr_rwsem); + device = idr_find(&fw_device_idr, MINOR(devt)); + up_read(&idr_rwsem); + + return device; +} + +static void fw_device_shutdown(struct work_struct *work) +{ + struct fw_device *device = + container_of(work, struct fw_device, work.work); + int minor = MINOR(device->device.devt); + + down_write(&idr_rwsem); + idr_remove(&fw_device_idr, minor); + up_write(&idr_rwsem); + + fw_device_cdev_remove(device); + device_for_each_child(&device->device, NULL, shutdown_unit); + device_unregister(&device->device); +} + +static struct device_type fw_device_type = { + .release = fw_device_release, +}; + +/* + * These defines control the retry behavior for reading the config + * rom. It shouldn't be necessary to tweak these; if the device + * doesn't respond to a config rom read within 10 seconds, it's not + * going to respond at all. As for the initial delay, a lot of + * devices will be able to respond within half a second after bus + * reset. On the other hand, it's not really worth being more + * aggressive than that, since it scales pretty well; if 10 devices + * are plugged in, they're all getting read within one second. + */ + +#define MAX_RETRIES 10 +#define RETRY_DELAY (3 * HZ) +#define INITIAL_DELAY (HZ / 2) + +static void fw_device_init(struct work_struct *work) +{ + struct fw_device *device = + container_of(work, struct fw_device, work.work); + int minor, err; + + /* + * All failure paths here set node->data to NULL, so that we + * don't try to do device_for_each_child() on a kfree()'d + * device. + */ + + if (read_bus_info_block(device) < 0) { + if (device->config_rom_retries < MAX_RETRIES) { + device->config_rom_retries++; + schedule_delayed_work(&device->work, RETRY_DELAY); + } else { + fw_notify("giving up on config rom for node id %x\n", + device->node_id); + if (device->node == device->card->root_node) + schedule_delayed_work(&device->card->work, 0); + fw_device_release(&device->device); + } + return; + } + + err = -ENOMEM; + down_write(&idr_rwsem); + if (idr_pre_get(&fw_device_idr, GFP_KERNEL)) + err = idr_get_new(&fw_device_idr, device, &minor); + up_write(&idr_rwsem); + if (err < 0) + goto error; + + device->device.bus = &fw_bus_type; + device->device.type = &fw_device_type; + device->device.parent = device->card->device; + device->device.devt = MKDEV(fw_cdev_major, minor); + snprintf(device->device.bus_id, sizeof(device->device.bus_id), + "fw%d", minor); + + init_fw_attribute_group(&device->device, + fw_device_attributes, + &device->attribute_group); + if (device_add(&device->device)) { + fw_error("Failed to add device.\n"); + goto error_with_cdev; + } + + create_units(device); + + /* + * Transition the device to running state. If it got pulled + * out from under us while we did the intialization work, we + * have to shut down the device again here. Normally, though, + * fw_node_event will be responsible for shutting it down when + * necessary. We have to use the atomic cmpxchg here to avoid + * racing with the FW_NODE_DESTROYED case in + * fw_node_event(). + */ + if (atomic_cmpxchg(&device->state, + FW_DEVICE_INITIALIZING, + FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) + fw_device_shutdown(&device->work.work); + else + fw_notify("created new fw device %s (%d config rom retries)\n", + device->device.bus_id, device->config_rom_retries); + + /* + * Reschedule the IRM work if we just finished reading the + * root node config rom. If this races with a bus reset we + * just end up running the IRM work a couple of extra times - + * pretty harmless. + */ + if (device->node == device->card->root_node) + schedule_delayed_work(&device->card->work, 0); + + return; + + error_with_cdev: + down_write(&idr_rwsem); + idr_remove(&fw_device_idr, minor); + up_write(&idr_rwsem); + error: + put_device(&device->device); +} + +static int update_unit(struct device *dev, void *data) +{ + struct fw_unit *unit = fw_unit(dev); + struct fw_driver *driver = (struct fw_driver *)dev->driver; + + if (is_fw_unit(dev) && driver != NULL && driver->update != NULL) { + down(&dev->sem); + driver->update(unit); + up(&dev->sem); + } + + return 0; +} + +static void fw_device_update(struct work_struct *work) +{ + struct fw_device *device = + container_of(work, struct fw_device, work.work); + + fw_device_cdev_update(device); + device_for_each_child(&device->device, NULL, update_unit); +} + +void fw_node_event(struct fw_card *card, struct fw_node *node, int event) +{ + struct fw_device *device; + + switch (event) { + case FW_NODE_CREATED: + case FW_NODE_LINK_ON: + if (!node->link_on) + break; + + device = kzalloc(sizeof(*device), GFP_ATOMIC); + if (device == NULL) + break; + + /* + * Do minimal intialization of the device here, the + * rest will happen in fw_device_init(). We need the + * card and node so we can read the config rom and we + * need to do device_initialize() now so + * device_for_each_child() in FW_NODE_UPDATED is + * doesn't freak out. + */ + device_initialize(&device->device); + atomic_set(&device->state, FW_DEVICE_INITIALIZING); + device->card = fw_card_get(card); + device->node = fw_node_get(node); + device->node_id = node->node_id; + device->generation = card->generation; + INIT_LIST_HEAD(&device->client_list); + + /* + * Set the node data to point back to this device so + * FW_NODE_UPDATED callbacks can update the node_id + * and generation for the device. + */ + node->data = device; + + /* + * Many devices are slow to respond after bus resets, + * especially if they are bus powered and go through + * power-up after getting plugged in. We schedule the + * first config rom scan half a second after bus reset. + */ + INIT_DELAYED_WORK(&device->work, fw_device_init); + schedule_delayed_work(&device->work, INITIAL_DELAY); + break; + + case FW_NODE_UPDATED: + if (!node->link_on || node->data == NULL) + break; + + device = node->data; + device->node_id = node->node_id; + device->generation = card->generation; + if (atomic_read(&device->state) == FW_DEVICE_RUNNING) { + PREPARE_DELAYED_WORK(&device->work, fw_device_update); + schedule_delayed_work(&device->work, 0); + } + break; + + case FW_NODE_DESTROYED: + case FW_NODE_LINK_OFF: + if (!node->data) + break; + + /* + * Destroy the device associated with the node. There + * are two cases here: either the device is fully + * initialized (FW_DEVICE_RUNNING) or we're in the + * process of reading its config rom + * (FW_DEVICE_INITIALIZING). If it is fully + * initialized we can reuse device->work to schedule a + * full fw_device_shutdown(). If not, there's work + * scheduled to read it's config rom, and we just put + * the device in shutdown state to have that code fail + * to create the device. + */ + device = node->data; + if (atomic_xchg(&device->state, + FW_DEVICE_SHUTDOWN) == FW_DEVICE_RUNNING) { + PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); + schedule_delayed_work(&device->work, 0); + } + break; + } +} diff --git a/drivers/firewire/fw-device.h b/drivers/firewire/fw-device.h new file mode 100644 index 000000000000..0ba9d64ccf4c --- /dev/null +++ b/drivers/firewire/fw-device.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005-2006 Kristian Hoegsberg <krh@bitplanet.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __fw_device_h +#define __fw_device_h + +#include <linux/fs.h> +#include <linux/cdev.h> +#include <asm/atomic.h> + +enum fw_device_state { + FW_DEVICE_INITIALIZING, + FW_DEVICE_RUNNING, + FW_DEVICE_SHUTDOWN, +}; + +struct fw_attribute_group { + struct attribute_group *groups[2]; + struct attribute_group group; + struct attribute *attrs[11]; +}; + +struct fw_device { + atomic_t state; + struct fw_node *node; + int node_id; + int generation; + struct fw_card *card; + struct device device; + struct list_head link; + struct list_head client_list; + u32 *config_rom; + size_t config_rom_length; + int config_rom_retries; + struct delayed_work work; + struct fw_attribute_group attribute_group; +}; + +static inline struct fw_device * +fw_device(struct device *dev) +{ + return container_of(dev, struct fw_device, device); +} + +static inline int +fw_device_is_shutdown(struct fw_device *device) +{ + return atomic_read(&device->state) == FW_DEVICE_SHUTDOWN; +} + +struct fw_device *fw_device_get(struct fw_device *device); +void fw_device_put(struct fw_device *device); +int fw_device_enable_phys_dma(struct fw_device *device); + +void fw_device_cdev_update(struct fw_device *device); +void fw_device_cdev_remove(struct fw_device *device); + +struct fw_device *fw_device_from_devt(dev_t devt); +extern int fw_cdev_major; + +struct fw_unit { + struct device device; + u32 *directory; + struct fw_attribute_group attribute_group; +}; + +static inline struct fw_unit * +fw_unit(struct device *dev) +{ + return container_of(dev, struct fw_unit, device); +} + +#define CSR_OFFSET 0x40 +#define CSR_LEAF 0x80 +#define CSR_DIRECTORY 0xc0 + +#define CSR_DESCRIPTOR 0x01 +#define CSR_VENDOR 0x03 +#define CSR_HARDWARE_VERSION 0x04 +#define CSR_NODE_CAPABILITIES 0x0c +#define CSR_UNIT 0x11 +#define CSR_SPECIFIER_ID 0x12 +#define CSR_VERSION 0x13 +#define CSR_DEPENDENT_INFO 0x14 +#define CSR_MODEL 0x17 +#define CSR_INSTANCE 0x18 + +#define SBP2_COMMAND_SET_SPECIFIER 0x38 +#define SBP2_COMMAND_SET 0x39 +#define SBP2_COMMAND_SET_REVISION 0x3b +#define SBP2_FIRMWARE_REVISION 0x3c + +struct fw_csr_iterator { + u32 *p; + u32 *end; +}; + +void fw_csr_iterator_init(struct fw_csr_iterator *ci, u32 *p); +int fw_csr_iterator_next(struct fw_csr_iterator *ci, + int *key, int *value); + +#define FW_MATCH_VENDOR 0x0001 +#define FW_MATCH_MODEL 0x0002 +#define FW_MATCH_SPECIFIER_ID 0x0004 +#define FW_MATCH_VERSION 0x0008 + +struct fw_device_id { + u32 match_flags; + u32 vendor; + u32 model; + u32 specifier_id; + u32 version; + void *driver_data; +}; + +struct fw_driver { + struct device_driver driver; + /* Called when the parent device sits through a bus reset. */ + void (*update) (struct fw_unit *unit); + const struct fw_device_id *id_table; +}; + +static inline struct fw_driver * +fw_driver(struct device_driver *drv) +{ + return container_of(drv, struct fw_driver, driver); +} + +extern const struct file_operations fw_device_ops; + +#endif /* __fw_device_h */ diff --git a/drivers/firewire/fw-iso.c b/drivers/firewire/fw-iso.c new file mode 100644 index 000000000000..2b640e9be6de --- /dev/null +++ b/drivers/firewire/fw-iso.c @@ -0,0 +1,163 @@ +/* + * Isochronous IO functionality + * + * Copyright (C) 2006 Kristian Hoegsberg <krh@bitplanet.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> + +#include "fw-transaction.h" +#include "fw-topology.h" +#include "fw-device.h" + +int +fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, + int page_count, enum dma_data_direction direction) +{ + int i, j, retval = -ENOMEM; + dma_addr_t address; + + buffer->page_count = page_count; + buffer->direction = direction; + + buffer->pages = kmalloc(page_count * sizeof(buffer->pages[0]), + GFP_KERNEL); + if (buffer->pages == NULL) + goto out; + + for (i = 0; i < buffer->page_count; i++) { + buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); + if (buffer->pages[i] == NULL) + goto out_pages; + + address = dma_map_page(card->device, buffer->pages[i], + 0, PAGE_SIZE, direction); + if (dma_mapping_error(address)) { + __free_page(buffer->pages[i]); + goto out_pages; + } + set_page_private(buffer->pages[i], address); + } + + return 0; + + out_pages: + for (j = 0; j < i; j++) { + address = page_private(buffer->pages[j]); + dma_unmap_page(card->device, address, + PAGE_SIZE, DMA_TO_DEVICE); + __free_page(buffer->pages[j]); + } + kfree(buffer->pages); + out: + buffer->pages = NULL; + return retval; +} + +int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma) +{ + unsigned long uaddr; + int i, retval; + + uaddr = vma->vm_start; + for (i = 0; i < buffer->page_count; i++) { + retval = vm_insert_page(vma, uaddr, buffer->pages[i]); + if (retval) + return retval; + uaddr += PAGE_SIZE; + } + + return 0; +} + +void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, + struct fw_card *card) +{ + int i; + dma_addr_t address; + + for (i = 0; i < buffer->page_count; i++) { + address = page_private(buffer->pages[i]); + dma_unmap_page(card->device, address, + PAGE_SIZE, DMA_TO_DEVICE); + __free_page(buffer->pages[i]); + } + + kfree(buffer->pages); + buffer->pages = NULL; +} + +struct fw_iso_context * +fw_iso_context_create(struct fw_card *card, int type, + int channel, int speed, size_t header_size, + fw_iso_callback_t callback, void *callback_data) +{ + struct fw_iso_context *ctx; + + ctx = card->driver->allocate_iso_context(card, type, header_size); + if (IS_ERR(ctx)) + return ctx; + + ctx->card = card; + ctx->type = type; + ctx->channel = channel; + ctx->speed = speed; + ctx->header_size = header_size; + ctx->callback = callback; + ctx->callback_data = callback_data; + + return ctx; +} +EXPORT_SYMBOL(fw_iso_context_create); + +void fw_iso_context_destroy(struct fw_iso_context *ctx) +{ + struct fw_card *card = ctx->card; + + card->driver->free_iso_context(ctx); +} +EXPORT_SYMBOL(fw_iso_context_destroy); + +int +fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags) +{ + return ctx->card->driver->start_iso(ctx, cycle, sync, tags); +} +EXPORT_SYMBOL(fw_iso_context_start); + +int +fw_iso_context_queue(struct fw_iso_context *ctx, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload) +{ + struct fw_card *card = ctx->card; + + return card->driver->queue_iso(ctx, packet, buffer, payload); +} +EXPORT_SYMBOL(fw_iso_context_queue); + +int +fw_iso_context_stop(struct fw_iso_context *ctx) +{ + return ctx->card->driver->stop_iso(ctx); +} +EXPORT_SYMBOL(fw_iso_context_stop); diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c new file mode 100644 index 000000000000..1f5c70461b8b --- /dev/null +++ b/drivers/firewire/fw-ohci.c @@ -0,0 +1,1943 @@ +/* + * Driver for OHCI 1394 controllers + * + * Copyright (C) 2003-2006 Kristian Hoegsberg <krh@bitplanet.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/poll.h> +#include <linux/dma-mapping.h> + +#include <asm/uaccess.h> +#include <asm/semaphore.h> + +#include "fw-transaction.h" +#include "fw-ohci.h" + +#define DESCRIPTOR_OUTPUT_MORE 0 +#define DESCRIPTOR_OUTPUT_LAST (1 << 12) +#define DESCRIPTOR_INPUT_MORE (2 << 12) +#define DESCRIPTOR_INPUT_LAST (3 << 12) +#define DESCRIPTOR_STATUS (1 << 11) +#define DESCRIPTOR_KEY_IMMEDIATE (2 << 8) +#define DESCRIPTOR_PING (1 << 7) +#define DESCRIPTOR_YY (1 << 6) +#define DESCRIPTOR_NO_IRQ (0 << 4) +#define DESCRIPTOR_IRQ_ERROR (1 << 4) +#define DESCRIPTOR_IRQ_ALWAYS (3 << 4) +#define DESCRIPTOR_BRANCH_ALWAYS (3 << 2) +#define DESCRIPTOR_WAIT (3 << 0) + +struct descriptor { + __le16 req_count; + __le16 control; + __le32 data_address; + __le32 branch_address; + __le16 res_count; + __le16 transfer_status; +} __attribute__((aligned(16))); + +struct db_descriptor { + __le16 first_size; + __le16 control; + __le16 second_req_count; + __le16 first_req_count; + __le32 branch_address; + __le16 second_res_count; + __le16 first_res_count; + __le32 reserved0; + __le32 first_buffer; + __le32 second_buffer; + __le32 reserved1; +} __attribute__((aligned(16))); + +#define CONTROL_SET(regs) (regs) +#define CONTROL_CLEAR(regs) ((regs) + 4) +#define COMMAND_PTR(regs) ((regs) + 12) +#define CONTEXT_MATCH(regs) ((regs) + 16) + +struct ar_buffer { + struct descriptor descriptor; + struct ar_buffer *next; + __le32 data[0]; +}; + +struct ar_context { + struct fw_ohci *ohci; + struct ar_buffer *current_buffer; + struct ar_buffer *last_buffer; + void *pointer; + u32 regs; + struct tasklet_struct tasklet; +}; + +struct context; + +typedef int (*descriptor_callback_t)(struct context *ctx, + struct descriptor *d, + struct descriptor *last); +struct context { + struct fw_ohci *ohci; + u32 regs; + + struct descriptor *buffer; + dma_addr_t buffer_bus; + size_t buffer_size; + struct descriptor *head_descriptor; + struct descriptor *tail_descriptor; + struct descriptor *tail_descriptor_last; + struct descriptor *prev_descriptor; + + descriptor_callback_t callback; + + struct tasklet_struct tasklet; +}; + +#define IT_HEADER_SY(v) ((v) << 0) +#define IT_HEADER_TCODE(v) ((v) << 4) +#define IT_HEADER_CHANNEL(v) ((v) << 8) +#define IT_HEADER_TAG(v) ((v) << 14) +#define IT_HEADER_SPEED(v) ((v) << 16) +#define IT_HEADER_DATA_LENGTH(v) ((v) << 16) + +struct iso_context { + struct fw_iso_context base; + struct context context; + void *header; + size_t header_length; +}; + +#define CONFIG_ROM_SIZE 1024 + +struct fw_ohci { + struct fw_card card; + + u32 version; + __iomem char *registers; + dma_addr_t self_id_bus; + __le32 *self_id_cpu; + struct tasklet_struct bus_reset_tasklet; + int node_id; + int generation; + int request_generation; + u32 bus_seconds; + + /* + * Spinlock for accessing fw_ohci data. Never call out of + * this driver with this lock held. + */ + spinlock_t lock; + u32 self_id_buffer[512]; + + /* Config rom buffers */ + __be32 *config_rom; + dma_addr_t config_rom_bus; + __be32 *next_config_rom; + dma_addr_t next_config_rom_bus; + u32 next_header; + + struct ar_context ar_request_ctx; + struct ar_context ar_response_ctx; + struct context at_request_ctx; + struct context at_response_ctx; + + u32 it_context_mask; + struct iso_context *it_context_list; + u32 ir_context_mask; + struct iso_context *ir_context_list; +}; + +static inline struct fw_ohci *fw_ohci(struct fw_card *card) +{ + return container_of(card, struct fw_ohci, card); +} + +#define IT_CONTEXT_CYCLE_MATCH_ENABLE 0x80000000 +#define IR_CONTEXT_BUFFER_FILL 0x80000000 +#define IR_CONTEXT_ISOCH_HEADER 0x40000000 +#define IR_CONTEXT_CYCLE_MATCH_ENABLE 0x20000000 +#define IR_CONTEXT_MULTI_CHANNEL_MODE 0x10000000 +#define IR_CONTEXT_DUAL_BUFFER_MODE 0x08000000 + +#define CONTEXT_RUN 0x8000 +#define CONTEXT_WAKE 0x1000 +#define CONTEXT_DEAD 0x0800 +#define CONTEXT_ACTIVE 0x0400 + +#define OHCI1394_MAX_AT_REQ_RETRIES 0x2 +#define OHCI1394_MAX_AT_RESP_RETRIES 0x2 +#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8 + +#define FW_OHCI_MAJOR 240 +#define OHCI1394_REGISTER_SIZE 0x800 +#define OHCI_LOOP_COUNT 500 +#define OHCI1394_PCI_HCI_Control 0x40 +#define SELF_ID_BUF_SIZE 0x800 +#define OHCI_TCODE_PHY_PACKET 0x0e +#define OHCI_VERSION_1_1 0x010010 +#define ISO_BUFFER_SIZE (64 * 1024) +#define AT_BUFFER_SIZE 4096 + +static char ohci_driver_name[] = KBUILD_MODNAME; + +static inline void reg_write(const struct fw_ohci *ohci, int offset, u32 data) +{ + writel(data, ohci->registers + offset); +} + +static inline u32 reg_read(const struct fw_ohci *ohci, int offset) +{ + return readl(ohci->registers + offset); +} + +static inline void flush_writes(const struct fw_ohci *ohci) +{ + /* Do a dummy read to flush writes. */ + reg_read(ohci, OHCI1394_Version); +} + +static int +ohci_update_phy_reg(struct fw_card *card, int addr, + int clear_bits, int set_bits) +{ + struct fw_ohci *ohci = fw_ohci(card); + u32 val, old; + + reg_write(ohci, OHCI1394_PhyControl, OHCI1394_PhyControl_Read(addr)); + msleep(2); + val = reg_read(ohci, OHCI1394_PhyControl); + if ((val & OHCI1394_PhyControl_ReadDone) == 0) { + fw_error("failed to set phy reg bits.\n"); + return -EBUSY; + } + + old = OHCI1394_PhyControl_ReadData(val); + old = (old & ~clear_bits) | set_bits; + reg_write(ohci, OHCI1394_PhyControl, + OHCI1394_PhyControl_Write(addr, old)); + + return 0; +} + +static int ar_context_add_page(struct ar_context *ctx) +{ + struct device *dev = ctx->ohci->card.device; + struct ar_buffer *ab; + dma_addr_t ab_bus; + size_t offset; + + ab = (struct ar_buffer *) __get_free_page(GFP_ATOMIC); + if (ab == NULL) + return -ENOMEM; + + ab_bus = dma_map_single(dev, ab, PAGE_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(ab_bus)) { + free_page((unsigned long) ab); + return -ENOMEM; + } + + memset(&ab->descriptor, 0, sizeof(ab->descriptor)); + ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE | + DESCRIPTOR_STATUS | + DESCRIPTOR_BRANCH_ALWAYS); + offset = offsetof(struct ar_buffer, data); + ab->descriptor.req_count = cpu_to_le16(PAGE_SIZE - offset); + ab->descriptor.data_address = cpu_to_le32(ab_bus + offset); + ab->descriptor.res_count = cpu_to_le16(PAGE_SIZE - offset); + ab->descriptor.branch_address = 0; + + dma_sync_single_for_device(dev, ab_bus, PAGE_SIZE, DMA_BIDIRECTIONAL); + + ctx->last_buffer->descriptor.branch_address = ab_bus | 1; + ctx->last_buffer->next = ab; + ctx->last_buffer = ab; + + reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); + flush_writes(ctx->ohci); + + return 0; +} + +static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer) +{ + struct fw_ohci *ohci = ctx->ohci; + struct fw_packet p; + u32 status, length, tcode; + + p.header[0] = le32_to_cpu(buffer[0]); + p.header[1] = le32_to_cpu(buffer[1]); + p.header[2] = le32_to_cpu(buffer[2]); + + tcode = (p.header[0] >> 4) & 0x0f; + switch (tcode) { + case TCODE_WRITE_QUADLET_REQUEST: + case TCODE_READ_QUADLET_RESPONSE: + p.header[3] = (__force __u32) buffer[3]; + p.header_length = 16; + p.payload_length = 0; + break; + + case TCODE_READ_BLOCK_REQUEST : + p.header[3] = le32_to_cpu(buffer[3]); + p.header_length = 16; + p.payload_length = 0; + break; + + case TCODE_WRITE_BLOCK_REQUEST: + case TCODE_READ_BLOCK_RESPONSE: + case TCODE_LOCK_REQUEST: + case TCODE_LOCK_RESPONSE: + p.header[3] = le32_to_cpu(buffer[3]); + p.header_length = 16; + p.payload_length = p.header[3] >> 16; + break; + + case TCODE_WRITE_RESPONSE: + case TCODE_READ_QUADLET_REQUEST: + case OHCI_TCODE_PHY_PACKET: + p.header_length = 12; + p.payload_length = 0; + break; + } + + p.payload = (void *) buffer + p.header_length; + + /* FIXME: What to do about evt_* errors? */ + length = (p.header_length + p.payload_length + 3) / 4; + status = le32_to_cpu(buffer[length]); + + p.ack = ((status >> 16) & 0x1f) - 16; + p.speed = (status >> 21) & 0x7; + p.timestamp = status & 0xffff; + p.generation = ohci->request_generation; + + /* + * The OHCI bus reset handler synthesizes a phy packet with + * the new generation number when a bus reset happens (see + * section 8.4.2.3). This helps us determine when a request + * was received and make sure we send the response in the same + * generation. We only need this for requests; for responses + * we use the unique tlabel for finding the matching + * request. + */ + + if (p.ack + 16 == 0x09) + ohci->request_generation = (buffer[2] >> 16) & 0xff; + else if (ctx == &ohci->ar_request_ctx) + fw_core_handle_request(&ohci->card, &p); + else + fw_core_handle_response(&ohci->card, &p); + + return buffer + length + 1; +} + +static void ar_context_tasklet(unsigned long data) +{ + struct ar_context *ctx = (struct ar_context *)data; + struct fw_ohci *ohci = ctx->ohci; + struct ar_buffer *ab; + struct descriptor *d; + void *buffer, *end; + + ab = ctx->current_buffer; + d = &ab->descriptor; + + if (d->res_count == 0) { + size_t size, rest, offset; + + /* + * This descriptor is finished and we may have a + * packet split across this and the next buffer. We + * reuse the page for reassembling the split packet. + */ + + offset = offsetof(struct ar_buffer, data); + dma_unmap_single(ohci->card.device, + ab->descriptor.data_address - offset, + PAGE_SIZE, DMA_BIDIRECTIONAL); + + buffer = ab; + ab = ab->next; + d = &ab->descriptor; + size = buffer + PAGE_SIZE - ctx->pointer; + rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count); + memmove(buffer, ctx->pointer, size); + memcpy(buffer + size, ab->data, rest); + ctx->current_buffer = ab; + ctx->pointer = (void *) ab->data + rest; + end = buffer + size + rest; + + while (buffer < end) + buffer = handle_ar_packet(ctx, buffer); + + free_page((unsigned long)buffer); + ar_context_add_page(ctx); + } else { + buffer = ctx->pointer; + ctx->pointer = end = + (void *) ab + PAGE_SIZE - le16_to_cpu(d->res_count); + + while (buffer < end) + buffer = handle_ar_packet(ctx, buffer); + } +} + +static int +ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, u32 regs) +{ + struct ar_buffer ab; + + ctx->regs = regs; + ctx->ohci = ohci; + ctx->last_buffer = &ab; + tasklet_init(&ctx->tasklet, ar_context_tasklet, (unsigned long)ctx); + + ar_context_add_page(ctx); + ar_context_add_page(ctx); + ctx->current_buffer = ab.next; + ctx->pointer = ctx->current_buffer->data; + + reg_write(ctx->ohci, COMMAND_PTR(ctx->regs), ab.descriptor.branch_address); + reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_RUN); + flush_writes(ctx->ohci); + + return 0; +} + +static void context_tasklet(unsigned long data) +{ + struct context *ctx = (struct context *) data; + struct fw_ohci *ohci = ctx->ohci; + struct descriptor *d, *last; + u32 address; + int z; + + dma_sync_single_for_cpu(ohci->card.device, ctx->buffer_bus, + ctx->buffer_size, DMA_TO_DEVICE); + + d = ctx->tail_descriptor; + last = ctx->tail_descriptor_last; + + while (last->branch_address != 0) { + address = le32_to_cpu(last->branch_address); + z = address & 0xf; + d = ctx->buffer + (address - ctx->buffer_bus) / sizeof(*d); + last = (z == 2) ? d : d + z - 1; + + if (!ctx->callback(ctx, d, last)) + break; + + ctx->tail_descriptor = d; + ctx->tail_descriptor_last = last; + } +} + +static int +context_init(struct context *ctx, struct fw_ohci *ohci, + size_t buffer_size, u32 regs, + descriptor_callback_t callback) +{ + ctx->ohci = ohci; + ctx->regs = regs; + ctx->buffer_size = buffer_size; + ctx->buffer = kmalloc(buffer_size, GFP_KERNEL); + if (ctx->buffer == NULL) + return -ENOMEM; + + tasklet_init(&ctx->tasklet, context_tasklet, (unsigned long)ctx); + ctx->callback = callback; + + ctx->buffer_bus = + dma_map_single(ohci->card.device, ctx->buffer, + buffer_size, DMA_TO_DEVICE); + if (dma_mapping_error(ctx->buffer_bus)) { + kfree(ctx->buffer); + return -ENOMEM; + } + + ctx->head_descriptor = ctx->buffer; + ctx->prev_descriptor = ctx->buffer; + ctx->tail_descriptor = ctx->buffer; + ctx->tail_descriptor_last = ctx->buffer; + + /* + * We put a dummy descriptor in the buffer that has a NULL + * branch address and looks like it's been sent. That way we + * have a descriptor to append DMA programs to. Also, the + * ring buffer invariant is that it always has at least one + * element so that head == tail means buffer full. + */ + + memset(ctx->head_descriptor, 0, sizeof(*ctx->head_descriptor)); + ctx->head_descriptor->control = cpu_to_le16(DESCRIPTOR_OUTPUT_LAST); + ctx->head_descriptor->transfer_status = cpu_to_le16(0x8011); + ctx->head_descriptor++; + + return 0; +} + +static void +context_release(struct context *ctx) +{ + struct fw_card *card = &ctx->ohci->card; + + dma_unmap_single(card->device, ctx->buffer_bus, + ctx->buffer_size, DMA_TO_DEVICE); + kfree(ctx->buffer); +} + +static struct descriptor * +context_get_descriptors(struct context *ctx, int z, dma_addr_t *d_bus) +{ + struct descriptor *d, *tail, *end; + + d = ctx->head_descriptor; + tail = ctx->tail_descriptor; + end = ctx->buffer + ctx->buffer_size / sizeof(*d); + + if (d + z <= tail) { + goto has_space; + } else if (d > tail && d + z <= end) { + goto has_space; + } else if (d > tail && ctx->buffer + z <= tail) { + d = ctx->buffer; + goto has_space; + } + + return NULL; + + has_space: + memset(d, 0, z * sizeof(*d)); + *d_bus = ctx->buffer_bus + (d - ctx->buffer) * sizeof(*d); + + return d; +} + +static void context_run(struct context *ctx, u32 extra) +{ + struct fw_ohci *ohci = ctx->ohci; + + reg_write(ohci, COMMAND_PTR(ctx->regs), + le32_to_cpu(ctx->tail_descriptor_last->branch_address)); + reg_write(ohci, CONTROL_CLEAR(ctx->regs), ~0); + reg_write(ohci, CONTROL_SET(ctx->regs), CONTEXT_RUN | extra); + flush_writes(ohci); +} + +static void context_append(struct context *ctx, + struct descriptor *d, int z, int extra) +{ + dma_addr_t d_bus; + + d_bus = ctx->buffer_bus + (d - ctx->buffer) * sizeof(*d); + + ctx->head_descriptor = d + z + extra; + ctx->prev_descriptor->branch_address = cpu_to_le32(d_bus | z); + ctx->prev_descriptor = z == 2 ? d : d + z - 1; + + dma_sync_single_for_device(ctx->ohci->card.device, ctx->buffer_bus, + ctx->buffer_size, DMA_TO_DEVICE); + + reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); + flush_writes(ctx->ohci); +} + +static void context_stop(struct context *ctx) +{ + u32 reg; + int i; + + reg_write(ctx->ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN); + flush_writes(ctx->ohci); + + for (i = 0; i < 10; i++) { + reg = reg_read(ctx->ohci, CONTROL_SET(ctx->regs)); + if ((reg & CONTEXT_ACTIVE) == 0) + break; + + fw_notify("context_stop: still active (0x%08x)\n", reg); + msleep(1); + } +} + +struct driver_data { + struct fw_packet *packet; +}; + +/* + * This function apppends a packet to the DMA queue for transmission. + * Must always be called with the ochi->lock held to ensure proper + * generation handling and locking around packet queue manipulation. + */ +static int +at_context_queue_packet(struct context *ctx, struct fw_packet *packet) +{ + struct fw_ohci *ohci = ctx->ohci; + dma_addr_t d_bus, payload_bus; + struct driver_data *driver_data; + struct descriptor *d, *last; + __le32 *header; + int z, tcode; + u32 reg; + + d = context_get_descriptors(ctx, 4, &d_bus); + if (d == NULL) { + packet->ack = RCODE_SEND_ERROR; + return -1; + } + + d[0].control = cpu_to_le16(DESCRIPTOR_KEY_IMMEDIATE); + d[0].res_count = cpu_to_le16(packet->timestamp); + + /* + * The DMA format for asyncronous link packets is different + * from the IEEE1394 layout, so shift the fields around + * accordingly. If header_length is 8, it's a PHY packet, to + * which we need to prepend an extra quadlet. + */ + + header = (__le32 *) &d[1]; + if (packet->header_length > 8) { + header[0] = cpu_to_le32((packet->header[0] & 0xffff) | + (packet->speed << 16)); + header[1] = cpu_to_le32((packet->header[1] & 0xffff) | + (packet->header[0] & 0xffff0000)); + header[2] = cpu_to_le32(packet->header[2]); + + tcode = (packet->header[0] >> 4) & 0x0f; + if (TCODE_IS_BLOCK_PACKET(tcode)) + header[3] = cpu_to_le32(packet->header[3]); + else + header[3] = (__force __le32) packet->header[3]; + + d[0].req_count = cpu_to_le16(packet->header_length); + } else { + header[0] = cpu_to_le32((OHCI1394_phy_tcode << 4) | + (packet->speed << 16)); + header[1] = cpu_to_le32(packet->header[0]); + header[2] = cpu_to_le32(packet->header[1]); + d[0].req_count = cpu_to_le16(12); + } + + driver_data = (struct driver_data *) &d[3]; + driver_data->packet = packet; + packet->driver_data = driver_data; + + if (packet->payload_length > 0) { + payload_bus = + dma_map_single(ohci->card.device, packet->payload, + packet->payload_length, DMA_TO_DEVICE); + if (dma_mapping_error(payload_bus)) { + packet->ack = RCODE_SEND_ERROR; + return -1; + } + + d[2].req_count = cpu_to_le16(packet->payload_length); + d[2].data_address = cpu_to_le32(payload_bus); + last = &d[2]; + z = 3; + } else { + last = &d[0]; + z = 2; + } + + last->control |= cpu_to_le16(DESCRIPTOR_OUTPUT_LAST | + DESCRIPTOR_IRQ_ALWAYS | + DESCRIPTOR_BRANCH_ALWAYS); + + /* FIXME: Document how the locking works. */ + if (ohci->generation != packet->generation) { + packet->ack = RCODE_GENERATION; + return -1; + } + + context_append(ctx, d, z, 4 - z); + + /* If the context isn't already running, start it up. */ + reg = reg_read(ctx->ohci, CONTROL_SET(ctx->regs)); + if ((reg & CONTEXT_RUN) == 0) + context_run(ctx, 0); + + return 0; +} + +static int handle_at_packet(struct context *context, + struct descriptor *d, + struct descriptor *last) +{ + struct driver_data *driver_data; + struct fw_packet *packet; + struct fw_ohci *ohci = context->ohci; + dma_addr_t payload_bus; + int evt; + + if (last->transfer_status == 0) + /* This descriptor isn't done yet, stop iteration. */ + return 0; + + driver_data = (struct driver_data *) &d[3]; + packet = driver_data->packet; + if (packet == NULL) + /* This packet was cancelled, just continue. */ + return 1; + + payload_bus = le32_to_cpu(last->data_address); + if (payload_bus != 0) + dma_unmap_single(ohci->card.device, payload_bus, + packet->payload_length, DMA_TO_DEVICE); + + evt = le16_to_cpu(last->transfer_status) & 0x1f; + packet->timestamp = le16_to_cpu(last->res_count); + + switch (evt) { + case OHCI1394_evt_timeout: + /* Async response transmit timed out. */ + packet->ack = RCODE_CANCELLED; + break; + + case OHCI1394_evt_flushed: + /* + * The packet was flushed should give same error as + * when we try to use a stale generation count. + */ + packet->ack = RCODE_GENERATION; + break; + + case OHCI1394_evt_missing_ack: + /* + * Using a valid (current) generation count, but the + * node is not on the bus or not sending acks. + */ + packet->ack = RCODE_NO_ACK; + break; + + case ACK_COMPLETE + 0x10: + case ACK_PENDING + 0x10: + case ACK_BUSY_X + 0x10: + case ACK_BUSY_A + 0x10: + case ACK_BUSY_B + 0x10: + case ACK_DATA_ERROR + 0x10: + case ACK_TYPE_ERROR + 0x10: + packet->ack = evt - 0x10; + break; + + default: + packet->ack = RCODE_SEND_ERROR; + break; + } + + packet->callback(packet, &ohci->card, packet->ack); + + return 1; +} + +#define HEADER_GET_DESTINATION(q) (((q) >> 16) & 0xffff) +#define HEADER_GET_TCODE(q) (((q) >> 4) & 0x0f) +#define HEADER_GET_OFFSET_HIGH(q) (((q) >> 0) & 0xffff) +#define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff) +#define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff) + +static void +handle_local_rom(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr) +{ + struct fw_packet response; + int tcode, length, i; + + tcode = HEADER_GET_TCODE(packet->header[0]); + if (TCODE_IS_BLOCK_PACKET(tcode)) + length = HEADER_GET_DATA_LENGTH(packet->header[3]); + else + length = 4; + + i = csr - CSR_CONFIG_ROM; + if (i + length > CONFIG_ROM_SIZE) { + fw_fill_response(&response, packet->header, + RCODE_ADDRESS_ERROR, NULL, 0); + } else if (!TCODE_IS_READ_REQUEST(tcode)) { + fw_fill_response(&response, packet->header, + RCODE_TYPE_ERROR, NULL, 0); + } else { + fw_fill_response(&response, packet->header, RCODE_COMPLETE, + (void *) ohci->config_rom + i, length); + } + + fw_core_handle_response(&ohci->card, &response); +} + +static void +handle_local_lock(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr) +{ + struct fw_packet response; + int tcode, length, ext_tcode, sel; + __be32 *payload, lock_old; + u32 lock_arg, lock_data; + + tcode = HEADER_GET_TCODE(packet->header[0]); + length = HEADER_GET_DATA_LENGTH(packet->header[3]); + payload = packet->payload; + ext_tcode = HEADER_GET_EXTENDED_TCODE(packet->header[3]); + + if (tcode == TCODE_LOCK_REQUEST && + ext_tcode == EXTCODE_COMPARE_SWAP && length == 8) { + lock_arg = be32_to_cpu(payload[0]); + lock_data = be32_to_cpu(payload[1]); + } else if (tcode == TCODE_READ_QUADLET_REQUEST) { + lock_arg = 0; + lock_data = 0; + } else { + fw_fill_response(&response, packet->header, + RCODE_TYPE_ERROR, NULL, 0); + goto out; + } + + sel = (csr - CSR_BUS_MANAGER_ID) / 4; + reg_write(ohci, OHCI1394_CSRData, lock_data); + reg_write(ohci, OHCI1394_CSRCompareData, lock_arg); + reg_write(ohci, OHCI1394_CSRControl, sel); + + if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000) + lock_old = cpu_to_be32(reg_read(ohci, OHCI1394_CSRData)); + else + fw_notify("swap not done yet\n"); + + fw_fill_response(&response, packet->header, + RCODE_COMPLETE, &lock_old, sizeof(lock_old)); + out: + fw_core_handle_response(&ohci->card, &response); +} + +static void +handle_local_request(struct context *ctx, struct fw_packet *packet) +{ + u64 offset; + u32 csr; + + if (ctx == &ctx->ohci->at_request_ctx) { + packet->ack = ACK_PENDING; + packet->callback(packet, &ctx->ohci->card, packet->ack); + } + + offset = + ((unsigned long long) + HEADER_GET_OFFSET_HIGH(packet->header[1]) << 32) | + packet->header[2]; + csr = offset - CSR_REGISTER_BASE; + + /* Handle config rom reads. */ + if (csr >= CSR_CONFIG_ROM && csr < CSR_CONFIG_ROM_END) + handle_local_rom(ctx->ohci, packet, csr); + else switch (csr) { + case CSR_BUS_MANAGER_ID: + case CSR_BANDWIDTH_AVAILABLE: + case CSR_CHANNELS_AVAILABLE_HI: + case CSR_CHANNELS_AVAILABLE_LO: + handle_local_lock(ctx->ohci, packet, csr); + break; + default: + if (ctx == &ctx->ohci->at_request_ctx) + fw_core_handle_request(&ctx->ohci->card, packet); + else + fw_core_handle_response(&ctx->ohci->card, packet); + break; + } + + if (ctx == &ctx->ohci->at_response_ctx) { + packet->ack = ACK_COMPLETE; + packet->callback(packet, &ctx->ohci->card, packet->ack); + } +} + +static void +at_context_transmit(struct context *ctx, struct fw_packet *packet) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&ctx->ohci->lock, flags); + + if (HEADER_GET_DESTINATION(packet->header[0]) == ctx->ohci->node_id && + ctx->ohci->generation == packet->generation) { + spin_unlock_irqrestore(&ctx->ohci->lock, flags); + handle_local_request(ctx, packet); + return; + } + + retval = at_context_queue_packet(ctx, packet); + spin_unlock_irqrestore(&ctx->ohci->lock, flags); + + if (retval < 0) + packet->callback(packet, &ctx->ohci->card, packet->ack); + +} + +static void bus_reset_tasklet(unsigned long data) +{ + struct fw_ohci *ohci = (struct fw_ohci *)data; + int self_id_count, i, j, reg; + int generation, new_generation; + unsigned long flags; + + reg = reg_read(ohci, OHCI1394_NodeID); + if (!(reg & OHCI1394_NodeID_idValid)) { + fw_error("node ID not valid, new bus reset in progress\n"); + return; + } + ohci->node_id = reg & 0xffff; + + /* + * The count in the SelfIDCount register is the number of + * bytes in the self ID receive buffer. Since we also receive + * the inverted quadlets and a header quadlet, we shift one + * bit extra to get the actual number of self IDs. + */ + + self_id_count = (reg_read(ohci, OHCI1394_SelfIDCount) >> 3) & 0x3ff; + generation = (le32_to_cpu(ohci->self_id_cpu[0]) >> 16) & 0xff; + + for (i = 1, j = 0; j < self_id_count; i += 2, j++) { + if (ohci->self_id_cpu[i] != ~ohci->self_id_cpu[i + 1]) + fw_error("inconsistent self IDs\n"); + ohci->self_id_buffer[j] = le32_to_cpu(ohci->self_id_cpu[i]); + } + + /* + * Check the consistency of the self IDs we just read. The + * problem we face is that a new bus reset can start while we + * read out the self IDs from the DMA buffer. If this happens, + * the DMA buffer will be overwritten with new self IDs and we + * will read out inconsistent data. The OHCI specification + * (section 11.2) recommends a technique similar to + * linux/seqlock.h, where we remember the generation of the + * self IDs in the buffer before reading them out and compare + * it to the current generation after reading them out. If + * the two generations match we know we have a consistent set + * of self IDs. + */ + + new_generation = (reg_read(ohci, OHCI1394_SelfIDCount) >> 16) & 0xff; + if (new_generation != generation) { + fw_notify("recursive bus reset detected, " + "discarding self ids\n"); + return; + } + + /* FIXME: Document how the locking works. */ + spin_lock_irqsave(&ohci->lock, flags); + + ohci->generation = generation; + context_stop(&ohci->at_request_ctx); + context_stop(&ohci->at_response_ctx); + reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); + + /* + * This next bit is unrelated to the AT context stuff but we + * have to do it under the spinlock also. If a new config rom + * was set up before this reset, the old one is now no longer + * in use and we can free it. Update the config rom pointers + * to point to the current config rom and clear the + * next_config_rom pointer so a new udpate can take place. + */ + + if (ohci->next_config_rom != NULL) { + dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, + ohci->config_rom, ohci->config_rom_bus); + ohci->config_rom = ohci->next_config_rom; + ohci->config_rom_bus = ohci->next_config_rom_bus; + ohci->next_config_rom = NULL; + + /* + * Restore config_rom image and manually update + * config_rom registers. Writing the header quadlet + * will indicate that the config rom is ready, so we + * do that last. + */ + reg_write(ohci, OHCI1394_BusOptions, + be32_to_cpu(ohci->config_rom[2])); + ohci->config_rom[0] = cpu_to_be32(ohci->next_header); + reg_write(ohci, OHCI1394_ConfigROMhdr, ohci->next_header); + } + + spin_unlock_irqrestore(&ohci->lock, flags); + + fw_core_handle_bus_reset(&ohci->card, ohci->node_id, generation, + self_id_count, ohci->self_id_buffer); +} + +static irqreturn_t irq_handler(int irq, void *data) +{ + struct fw_ohci *ohci = data; + u32 event, iso_event, cycle_time; + int i; + + event = reg_read(ohci, OHCI1394_IntEventClear); + + if (!event) + return IRQ_NONE; + + reg_write(ohci, OHCI1394_IntEventClear, event); + + if (event & OHCI1394_selfIDComplete) + tasklet_schedule(&ohci->bus_reset_tasklet); + + if (event & OHCI1394_RQPkt) + tasklet_schedule(&ohci->ar_request_ctx.tasklet); + + if (event & OHCI1394_RSPkt) + tasklet_schedule(&ohci->ar_response_ctx.tasklet); + + if (event & OHCI1394_reqTxComplete) + tasklet_schedule(&ohci->at_request_ctx.tasklet); + + if (event & OHCI1394_respTxComplete) + tasklet_schedule(&ohci->at_response_ctx.tasklet); + + iso_event = reg_read(ohci, OHCI1394_IsoRecvIntEventClear); + reg_write(ohci, OHCI1394_IsoRecvIntEventClear, iso_event); + + while (iso_event) { + i = ffs(iso_event) - 1; + tasklet_schedule(&ohci->ir_context_list[i].context.tasklet); + iso_event &= ~(1 << i); + } + + iso_event = reg_read(ohci, OHCI1394_IsoXmitIntEventClear); + reg_write(ohci, OHCI1394_IsoXmitIntEventClear, iso_event); + + while (iso_event) { + i = ffs(iso_event) - 1; + tasklet_schedule(&ohci->it_context_list[i].context.tasklet); + iso_event &= ~(1 << i); + } + + if (event & OHCI1394_cycle64Seconds) { + cycle_time = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + if ((cycle_time & 0x80000000) == 0) + ohci->bus_seconds++; + } + + return IRQ_HANDLED; +} + +static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length) +{ + struct fw_ohci *ohci = fw_ohci(card); + struct pci_dev *dev = to_pci_dev(card->device); + + /* + * When the link is not yet enabled, the atomic config rom + * update mechanism described below in ohci_set_config_rom() + * is not active. We have to update ConfigRomHeader and + * BusOptions manually, and the write to ConfigROMmap takes + * effect immediately. We tie this to the enabling of the + * link, so we have a valid config rom before enabling - the + * OHCI requires that ConfigROMhdr and BusOptions have valid + * values before enabling. + * + * However, when the ConfigROMmap is written, some controllers + * always read back quadlets 0 and 2 from the config rom to + * the ConfigRomHeader and BusOptions registers on bus reset. + * They shouldn't do that in this initial case where the link + * isn't enabled. This means we have to use the same + * workaround here, setting the bus header to 0 and then write + * the right values in the bus reset tasklet. + */ + + ohci->next_config_rom = + dma_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE, + &ohci->next_config_rom_bus, GFP_KERNEL); + if (ohci->next_config_rom == NULL) + return -ENOMEM; + + memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE); + fw_memcpy_to_be32(ohci->next_config_rom, config_rom, length * 4); + + ohci->next_header = config_rom[0]; + ohci->next_config_rom[0] = 0; + reg_write(ohci, OHCI1394_ConfigROMhdr, 0); + reg_write(ohci, OHCI1394_BusOptions, config_rom[2]); + reg_write(ohci, OHCI1394_ConfigROMmap, ohci->next_config_rom_bus); + + reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000); + + if (request_irq(dev->irq, irq_handler, + IRQF_SHARED, ohci_driver_name, ohci)) { + fw_error("Failed to allocate shared interrupt %d.\n", + dev->irq); + dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, + ohci->config_rom, ohci->config_rom_bus); + return -EIO; + } + + reg_write(ohci, OHCI1394_HCControlSet, + OHCI1394_HCControl_linkEnable | + OHCI1394_HCControl_BIBimageValid); + flush_writes(ohci); + + /* + * We are ready to go, initiate bus reset to finish the + * initialization. + */ + + fw_core_initiate_bus_reset(&ohci->card, 1); + + return 0; +} + +static int +ohci_set_config_rom(struct fw_card *card, u32 *config_rom, size_t length) +{ + struct fw_ohci *ohci; + unsigned long flags; + int retval = 0; + __be32 *next_config_rom; + dma_addr_t next_config_rom_bus; + + ohci = fw_ohci(card); + + /* + * When the OHCI controller is enabled, the config rom update + * mechanism is a bit tricky, but easy enough to use. See + * section 5.5.6 in the OHCI specification. + * + * The OHCI controller caches the new config rom address in a + * shadow register (ConfigROMmapNext) and needs a bus reset + * for the changes to take place. When the bus reset is + * detected, the controller loads the new values for the + * ConfigRomHeader and BusOptions registers from the specified + * config rom and loads ConfigROMmap from the ConfigROMmapNext + * shadow register. All automatically and atomically. + * + * Now, there's a twist to this story. The automatic load of + * ConfigRomHeader and BusOptions doesn't honor the + * noByteSwapData bit, so with a be32 config rom, the + * controller will load be32 values in to these registers + * during the atomic update, even on litte endian + * architectures. The workaround we use is to put a 0 in the + * header quadlet; 0 is endian agnostic and means that the + * config rom isn't ready yet. In the bus reset tasklet we + * then set up the real values for the two registers. + * + * We use ohci->lock to avoid racing with the code that sets + * ohci->next_config_rom to NULL (see bus_reset_tasklet). + */ + + next_config_rom = + dma_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE, + &next_config_rom_bus, GFP_KERNEL); + if (next_config_rom == NULL) + return -ENOMEM; + + spin_lock_irqsave(&ohci->lock, flags); + + if (ohci->next_config_rom == NULL) { + ohci->next_config_rom = next_config_rom; + ohci->next_config_rom_bus = next_config_rom_bus; + + memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE); + fw_memcpy_to_be32(ohci->next_config_rom, config_rom, + length * 4); + + ohci->next_header = config_rom[0]; + ohci->next_config_rom[0] = 0; + + reg_write(ohci, OHCI1394_ConfigROMmap, + ohci->next_config_rom_bus); + } else { + dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, + next_config_rom, next_config_rom_bus); + retval = -EBUSY; + } + + spin_unlock_irqrestore(&ohci->lock, flags); + + /* + * Now initiate a bus reset to have the changes take + * effect. We clean up the old config rom memory and DMA + * mappings in the bus reset tasklet, since the OHCI + * controller could need to access it before the bus reset + * takes effect. + */ + if (retval == 0) + fw_core_initiate_bus_reset(&ohci->card, 1); + + return retval; +} + +static void ohci_send_request(struct fw_card *card, struct fw_packet *packet) +{ + struct fw_ohci *ohci = fw_ohci(card); + + at_context_transmit(&ohci->at_request_ctx, packet); +} + +static void ohci_send_response(struct fw_card *card, struct fw_packet *packet) +{ + struct fw_ohci *ohci = fw_ohci(card); + + at_context_transmit(&ohci->at_response_ctx, packet); +} + +static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet) +{ + struct fw_ohci *ohci = fw_ohci(card); + struct context *ctx = &ohci->at_request_ctx; + struct driver_data *driver_data = packet->driver_data; + int retval = -ENOENT; + + tasklet_disable(&ctx->tasklet); + + if (packet->ack != 0) + goto out; + + driver_data->packet = NULL; + packet->ack = RCODE_CANCELLED; + packet->callback(packet, &ohci->card, packet->ack); + retval = 0; + + out: + tasklet_enable(&ctx->tasklet); + + return retval; +} + +static int +ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation) +{ + struct fw_ohci *ohci = fw_ohci(card); + unsigned long flags; + int n, retval = 0; + + /* + * FIXME: Make sure this bitmask is cleared when we clear the busReset + * interrupt bit. Clear physReqResourceAllBuses on bus reset. + */ + + spin_lock_irqsave(&ohci->lock, flags); + + if (ohci->generation != generation) { + retval = -ESTALE; + goto out; + } + + /* + * Note, if the node ID contains a non-local bus ID, physical DMA is + * enabled for _all_ nodes on remote buses. + */ + + n = (node_id & 0xffc0) == LOCAL_BUS ? node_id & 0x3f : 63; + if (n < 32) + reg_write(ohci, OHCI1394_PhyReqFilterLoSet, 1 << n); + else + reg_write(ohci, OHCI1394_PhyReqFilterHiSet, 1 << (n - 32)); + + flush_writes(ohci); + out: + spin_unlock_irqrestore(&ohci->lock, flags); + return retval; +} + +static u64 +ohci_get_bus_time(struct fw_card *card) +{ + struct fw_ohci *ohci = fw_ohci(card); + u32 cycle_time; + u64 bus_time; + + cycle_time = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + bus_time = ((u64) ohci->bus_seconds << 32) | cycle_time; + + return bus_time; +} + +static int handle_ir_dualbuffer_packet(struct context *context, + struct descriptor *d, + struct descriptor *last) +{ + struct iso_context *ctx = + container_of(context, struct iso_context, context); + struct db_descriptor *db = (struct db_descriptor *) d; + __le32 *ir_header; + size_t header_length; + void *p, *end; + int i; + + if (db->first_res_count > 0 && db->second_res_count > 0) + /* This descriptor isn't done yet, stop iteration. */ + return 0; + + header_length = le16_to_cpu(db->first_req_count) - + le16_to_cpu(db->first_res_count); + + i = ctx->header_length; + p = db + 1; + end = p + header_length; + while (p < end && i + ctx->base.header_size <= PAGE_SIZE) { + /* + * The iso header is byteswapped to little endian by + * the controller, but the remaining header quadlets + * are big endian. We want to present all the headers + * as big endian, so we have to swap the first + * quadlet. + */ + *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4)); + memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4); + i += ctx->base.header_size; + p += ctx->base.header_size + 4; + } + + ctx->header_length = i; + + if (le16_to_cpu(db->control) & DESCRIPTOR_IRQ_ALWAYS) { + ir_header = (__le32 *) (db + 1); + ctx->base.callback(&ctx->base, + le32_to_cpu(ir_header[0]) & 0xffff, + ctx->header_length, ctx->header, + ctx->base.callback_data); + ctx->header_length = 0; + } + + return 1; +} + +static int handle_it_packet(struct context *context, + struct descriptor *d, + struct descriptor *last) +{ + struct iso_context *ctx = + container_of(context, struct iso_context, context); + + if (last->transfer_status == 0) + /* This descriptor isn't done yet, stop iteration. */ + return 0; + + if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) + ctx->base.callback(&ctx->base, le16_to_cpu(last->res_count), + 0, NULL, ctx->base.callback_data); + + return 1; +} + +static struct fw_iso_context * +ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size) +{ + struct fw_ohci *ohci = fw_ohci(card); + struct iso_context *ctx, *list; + descriptor_callback_t callback; + u32 *mask, regs; + unsigned long flags; + int index, retval = -ENOMEM; + + if (type == FW_ISO_CONTEXT_TRANSMIT) { + mask = &ohci->it_context_mask; + list = ohci->it_context_list; + callback = handle_it_packet; + } else { + mask = &ohci->ir_context_mask; + list = ohci->ir_context_list; + callback = handle_ir_dualbuffer_packet; + } + + /* FIXME: We need a fallback for pre 1.1 OHCI. */ + if (callback == handle_ir_dualbuffer_packet && + ohci->version < OHCI_VERSION_1_1) + return ERR_PTR(-EINVAL); + + spin_lock_irqsave(&ohci->lock, flags); + index = ffs(*mask) - 1; + if (index >= 0) + *mask &= ~(1 << index); + spin_unlock_irqrestore(&ohci->lock, flags); + + if (index < 0) + return ERR_PTR(-EBUSY); + + if (type == FW_ISO_CONTEXT_TRANSMIT) + regs = OHCI1394_IsoXmitContextBase(index); + else + regs = OHCI1394_IsoRcvContextBase(index); + + ctx = &list[index]; + memset(ctx, 0, sizeof(*ctx)); + ctx->header_length = 0; + ctx->header = (void *) __get_free_page(GFP_KERNEL); + if (ctx->header == NULL) + goto out; + + retval = context_init(&ctx->context, ohci, ISO_BUFFER_SIZE, + regs, callback); + if (retval < 0) + goto out_with_header; + + return &ctx->base; + + out_with_header: + free_page((unsigned long)ctx->header); + out: + spin_lock_irqsave(&ohci->lock, flags); + *mask |= 1 << index; + spin_unlock_irqrestore(&ohci->lock, flags); + + return ERR_PTR(retval); +} + +static int ohci_start_iso(struct fw_iso_context *base, + s32 cycle, u32 sync, u32 tags) +{ + struct iso_context *ctx = container_of(base, struct iso_context, base); + struct fw_ohci *ohci = ctx->context.ohci; + u32 control, match; + int index; + + if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { + index = ctx - ohci->it_context_list; + match = 0; + if (cycle >= 0) + match = IT_CONTEXT_CYCLE_MATCH_ENABLE | + (cycle & 0x7fff) << 16; + + reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 1 << index); + reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << index); + context_run(&ctx->context, match); + } else { + index = ctx - ohci->ir_context_list; + control = IR_CONTEXT_DUAL_BUFFER_MODE | IR_CONTEXT_ISOCH_HEADER; + match = (tags << 28) | (sync << 8) | ctx->base.channel; + if (cycle >= 0) { + match |= (cycle & 0x07fff) << 12; + control |= IR_CONTEXT_CYCLE_MATCH_ENABLE; + } + + reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 1 << index); + reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << index); + reg_write(ohci, CONTEXT_MATCH(ctx->context.regs), match); + context_run(&ctx->context, control); + } + + return 0; +} + +static int ohci_stop_iso(struct fw_iso_context *base) +{ + struct fw_ohci *ohci = fw_ohci(base->card); + struct iso_context *ctx = container_of(base, struct iso_context, base); + int index; + + if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { + index = ctx - ohci->it_context_list; + reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 1 << index); + } else { + index = ctx - ohci->ir_context_list; + reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 1 << index); + } + flush_writes(ohci); + context_stop(&ctx->context); + + return 0; +} + +static void ohci_free_iso_context(struct fw_iso_context *base) +{ + struct fw_ohci *ohci = fw_ohci(base->card); + struct iso_context *ctx = container_of(base, struct iso_context, base); + unsigned long flags; + int index; + + ohci_stop_iso(base); + context_release(&ctx->context); + free_page((unsigned long)ctx->header); + + spin_lock_irqsave(&ohci->lock, flags); + + if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { + index = ctx - ohci->it_context_list; + ohci->it_context_mask |= 1 << index; + } else { + index = ctx - ohci->ir_context_list; + ohci->ir_context_mask |= 1 << index; + } + + spin_unlock_irqrestore(&ohci->lock, flags); +} + +static int +ohci_queue_iso_transmit(struct fw_iso_context *base, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload) +{ + struct iso_context *ctx = container_of(base, struct iso_context, base); + struct descriptor *d, *last, *pd; + struct fw_iso_packet *p; + __le32 *header; + dma_addr_t d_bus, page_bus; + u32 z, header_z, payload_z, irq; + u32 payload_index, payload_end_index, next_page_index; + int page, end_page, i, length, offset; + + /* + * FIXME: Cycle lost behavior should be configurable: lose + * packet, retransmit or terminate.. + */ + + p = packet; + payload_index = payload; + + if (p->skip) + z = 1; + else + z = 2; + if (p->header_length > 0) + z++; + + /* Determine the first page the payload isn't contained in. */ + end_page = PAGE_ALIGN(payload_index + p->payload_length) >> PAGE_SHIFT; + if (p->payload_length > 0) + payload_z = end_page - (payload_index >> PAGE_SHIFT); + else + payload_z = 0; + + z += payload_z; + + /* Get header size in number of descriptors. */ + header_z = DIV_ROUND_UP(p->header_length, sizeof(*d)); + + d = context_get_descriptors(&ctx->context, z + header_z, &d_bus); + if (d == NULL) + return -ENOMEM; + + if (!p->skip) { + d[0].control = cpu_to_le16(DESCRIPTOR_KEY_IMMEDIATE); + d[0].req_count = cpu_to_le16(8); + + header = (__le32 *) &d[1]; + header[0] = cpu_to_le32(IT_HEADER_SY(p->sy) | + IT_HEADER_TAG(p->tag) | + IT_HEADER_TCODE(TCODE_STREAM_DATA) | + IT_HEADER_CHANNEL(ctx->base.channel) | + IT_HEADER_SPEED(ctx->base.speed)); + header[1] = + cpu_to_le32(IT_HEADER_DATA_LENGTH(p->header_length + + p->payload_length)); + } + + if (p->header_length > 0) { + d[2].req_count = cpu_to_le16(p->header_length); + d[2].data_address = cpu_to_le32(d_bus + z * sizeof(*d)); + memcpy(&d[z], p->header, p->header_length); + } + + pd = d + z - payload_z; + payload_end_index = payload_index + p->payload_length; + for (i = 0; i < payload_z; i++) { + page = payload_index >> PAGE_SHIFT; + offset = payload_index & ~PAGE_MASK; + next_page_index = (page + 1) << PAGE_SHIFT; + length = + min(next_page_index, payload_end_index) - payload_index; + pd[i].req_count = cpu_to_le16(length); + + page_bus = page_private(buffer->pages[page]); + pd[i].data_address = cpu_to_le32(page_bus + offset); + + payload_index += length; + } + + if (p->interrupt) + irq = DESCRIPTOR_IRQ_ALWAYS; + else + irq = DESCRIPTOR_NO_IRQ; + + last = z == 2 ? d : d + z - 1; + last->control |= cpu_to_le16(DESCRIPTOR_OUTPUT_LAST | + DESCRIPTOR_STATUS | + DESCRIPTOR_BRANCH_ALWAYS | + irq); + + context_append(&ctx->context, d, z, header_z); + + return 0; +} + +static int +ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload) +{ + struct iso_context *ctx = container_of(base, struct iso_context, base); + struct db_descriptor *db = NULL; + struct descriptor *d; + struct fw_iso_packet *p; + dma_addr_t d_bus, page_bus; + u32 z, header_z, length, rest; + int page, offset, packet_count, header_size; + + /* + * FIXME: Cycle lost behavior should be configurable: lose + * packet, retransmit or terminate.. + */ + + if (packet->skip) { + d = context_get_descriptors(&ctx->context, 2, &d_bus); + if (d == NULL) + return -ENOMEM; + + db = (struct db_descriptor *) d; + db->control = cpu_to_le16(DESCRIPTOR_STATUS | + DESCRIPTOR_BRANCH_ALWAYS | + DESCRIPTOR_WAIT); + db->first_size = cpu_to_le16(ctx->base.header_size + 4); + context_append(&ctx->context, d, 2, 0); + } + + p = packet; + z = 2; + + /* + * The OHCI controller puts the status word in the header + * buffer too, so we need 4 extra bytes per packet. + */ + packet_count = p->header_length / ctx->base.header_size; + header_size = packet_count * (ctx->base.header_size + 4); + + /* Get header size in number of descriptors. */ + header_z = DIV_ROUND_UP(header_size, sizeof(*d)); + page = payload >> PAGE_SHIFT; + offset = payload & ~PAGE_MASK; + rest = p->payload_length; + + /* FIXME: OHCI 1.0 doesn't support dual buffer receive */ + /* FIXME: make packet-per-buffer/dual-buffer a context option */ + while (rest > 0) { + d = context_get_descriptors(&ctx->context, + z + header_z, &d_bus); + if (d == NULL) + return -ENOMEM; + + db = (struct db_descriptor *) d; + db->control = cpu_to_le16(DESCRIPTOR_STATUS | + DESCRIPTOR_BRANCH_ALWAYS); + db->first_size = cpu_to_le16(ctx->base.header_size + 4); + db->first_req_count = cpu_to_le16(header_size); + db->first_res_count = db->first_req_count; + db->first_buffer = cpu_to_le32(d_bus + sizeof(*db)); + + if (offset + rest < PAGE_SIZE) + length = rest; + else + length = PAGE_SIZE - offset; + + db->second_req_count = cpu_to_le16(length); + db->second_res_count = db->second_req_count; + page_bus = page_private(buffer->pages[page]); + db->second_buffer = cpu_to_le32(page_bus + offset); + + if (p->interrupt && length == rest) + db->control |= cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS); + + context_append(&ctx->context, d, z, header_z); + offset = (offset + length) & ~PAGE_MASK; + rest -= length; + page++; + } + + return 0; +} + +static int +ohci_queue_iso(struct fw_iso_context *base, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload) +{ + struct iso_context *ctx = container_of(base, struct iso_context, base); + + if (base->type == FW_ISO_CONTEXT_TRANSMIT) + return ohci_queue_iso_transmit(base, packet, buffer, payload); + else if (ctx->context.ohci->version >= OHCI_VERSION_1_1) + return ohci_queue_iso_receive_dualbuffer(base, packet, + buffer, payload); + else + /* FIXME: Implement fallback for OHCI 1.0 controllers. */ + return -EINVAL; +} + +static const struct fw_card_driver ohci_driver = { + .name = ohci_driver_name, + .enable = ohci_enable, + .update_phy_reg = ohci_update_phy_reg, + .set_config_rom = ohci_set_config_rom, + .send_request = ohci_send_request, + .send_response = ohci_send_response, + .cancel_packet = ohci_cancel_packet, + .enable_phys_dma = ohci_enable_phys_dma, + .get_bus_time = ohci_get_bus_time, + + .allocate_iso_context = ohci_allocate_iso_context, + .free_iso_context = ohci_free_iso_context, + .queue_iso = ohci_queue_iso, + .start_iso = ohci_start_iso, + .stop_iso = ohci_stop_iso, +}; + +static int software_reset(struct fw_ohci *ohci) +{ + int i; + + reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset); + + for (i = 0; i < OHCI_LOOP_COUNT; i++) { + if ((reg_read(ohci, OHCI1394_HCControlSet) & + OHCI1394_HCControl_softReset) == 0) + return 0; + msleep(1); + } + + return -EBUSY; +} + +static int __devinit +pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) +{ + struct fw_ohci *ohci; + u32 bus_options, max_receive, link_speed; + u64 guid; + int err; + size_t size; + + ohci = kzalloc(sizeof(*ohci), GFP_KERNEL); + if (ohci == NULL) { + fw_error("Could not malloc fw_ohci data.\n"); + return -ENOMEM; + } + + fw_card_initialize(&ohci->card, &ohci_driver, &dev->dev); + + err = pci_enable_device(dev); + if (err) { + fw_error("Failed to enable OHCI hardware.\n"); + goto fail_put_card; + } + + pci_set_master(dev); + pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0); + pci_set_drvdata(dev, ohci); + + spin_lock_init(&ohci->lock); + + tasklet_init(&ohci->bus_reset_tasklet, + bus_reset_tasklet, (unsigned long)ohci); + + err = pci_request_region(dev, 0, ohci_driver_name); + if (err) { + fw_error("MMIO resource unavailable\n"); + goto fail_disable; + } + + ohci->registers = pci_iomap(dev, 0, OHCI1394_REGISTER_SIZE); + if (ohci->registers == NULL) { + fw_error("Failed to remap registers\n"); + err = -ENXIO; + goto fail_iomem; + } + + if (software_reset(ohci)) { + fw_error("Failed to reset ohci card.\n"); + err = -EBUSY; + goto fail_registers; + } + + /* + * Now enable LPS, which we need in order to start accessing + * most of the registers. In fact, on some cards (ALI M5251), + * accessing registers in the SClk domain without LPS enabled + * will lock up the machine. Wait 50msec to make sure we have + * full link enabled. + */ + reg_write(ohci, OHCI1394_HCControlSet, + OHCI1394_HCControl_LPS | + OHCI1394_HCControl_postedWriteEnable); + flush_writes(ohci); + msleep(50); + + reg_write(ohci, OHCI1394_HCControlClear, + OHCI1394_HCControl_noByteSwapData); + + reg_write(ohci, OHCI1394_LinkControlSet, + OHCI1394_LinkControl_rcvSelfID | + OHCI1394_LinkControl_cycleTimerEnable | + OHCI1394_LinkControl_cycleMaster); + + ar_context_init(&ohci->ar_request_ctx, ohci, + OHCI1394_AsReqRcvContextControlSet); + + ar_context_init(&ohci->ar_response_ctx, ohci, + OHCI1394_AsRspRcvContextControlSet); + + context_init(&ohci->at_request_ctx, ohci, AT_BUFFER_SIZE, + OHCI1394_AsReqTrContextControlSet, handle_at_packet); + + context_init(&ohci->at_response_ctx, ohci, AT_BUFFER_SIZE, + OHCI1394_AsRspTrContextControlSet, handle_at_packet); + + reg_write(ohci, OHCI1394_ATRetries, + OHCI1394_MAX_AT_REQ_RETRIES | + (OHCI1394_MAX_AT_RESP_RETRIES << 4) | + (OHCI1394_MAX_PHYS_RESP_RETRIES << 8)); + + reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0); + ohci->it_context_mask = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet); + reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, ~0); + size = sizeof(struct iso_context) * hweight32(ohci->it_context_mask); + ohci->it_context_list = kzalloc(size, GFP_KERNEL); + + reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0); + ohci->ir_context_mask = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet); + reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0); + size = sizeof(struct iso_context) * hweight32(ohci->ir_context_mask); + ohci->ir_context_list = kzalloc(size, GFP_KERNEL); + + if (ohci->it_context_list == NULL || ohci->ir_context_list == NULL) { + fw_error("Out of memory for it/ir contexts.\n"); + err = -ENOMEM; + goto fail_registers; + } + + /* self-id dma buffer allocation */ + ohci->self_id_cpu = dma_alloc_coherent(ohci->card.device, + SELF_ID_BUF_SIZE, + &ohci->self_id_bus, + GFP_KERNEL); + if (ohci->self_id_cpu == NULL) { + fw_error("Out of memory for self ID buffer.\n"); + err = -ENOMEM; + goto fail_registers; + } + + reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus); + reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000); + reg_write(ohci, OHCI1394_IntEventClear, ~0); + reg_write(ohci, OHCI1394_IntMaskClear, ~0); + reg_write(ohci, OHCI1394_IntMaskSet, + OHCI1394_selfIDComplete | + OHCI1394_RQPkt | OHCI1394_RSPkt | + OHCI1394_reqTxComplete | OHCI1394_respTxComplete | + OHCI1394_isochRx | OHCI1394_isochTx | + OHCI1394_masterIntEnable | + OHCI1394_cycle64Seconds); + + bus_options = reg_read(ohci, OHCI1394_BusOptions); + max_receive = (bus_options >> 12) & 0xf; + link_speed = bus_options & 0x7; + guid = ((u64) reg_read(ohci, OHCI1394_GUIDHi) << 32) | + reg_read(ohci, OHCI1394_GUIDLo); + + err = fw_card_add(&ohci->card, max_receive, link_speed, guid); + if (err < 0) + goto fail_self_id; + + ohci->version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; + fw_notify("Added fw-ohci device %s, OHCI version %x.%x\n", + dev->dev.bus_id, ohci->version >> 16, ohci->version & 0xff); + + return 0; + + fail_self_id: + dma_free_coherent(ohci->card.device, SELF_ID_BUF_SIZE, + ohci->self_id_cpu, ohci->self_id_bus); + fail_registers: + kfree(ohci->it_context_list); + kfree(ohci->ir_context_list); + pci_iounmap(dev, ohci->registers); + fail_iomem: + pci_release_region(dev, 0); + fail_disable: + pci_disable_device(dev); + fail_put_card: + fw_card_put(&ohci->card); + + return err; +} + +static void pci_remove(struct pci_dev *dev) +{ + struct fw_ohci *ohci; + + ohci = pci_get_drvdata(dev); + reg_write(ohci, OHCI1394_IntMaskClear, ~0); + flush_writes(ohci); + fw_core_remove_card(&ohci->card); + + /* + * FIXME: Fail all pending packets here, now that the upper + * layers can't queue any more. + */ + + software_reset(ohci); + free_irq(dev->irq, ohci); + dma_free_coherent(ohci->card.device, SELF_ID_BUF_SIZE, + ohci->self_id_cpu, ohci->self_id_bus); + kfree(ohci->it_context_list); + kfree(ohci->ir_context_list); + pci_iounmap(dev, ohci->registers); + pci_release_region(dev, 0); + pci_disable_device(dev); + fw_card_put(&ohci->card); + + fw_notify("Removed fw-ohci device.\n"); +} + +static struct pci_device_id pci_table[] = { + { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_FIREWIRE_OHCI, ~0) }, + { } +}; + +MODULE_DEVICE_TABLE(pci, pci_table); + +static struct pci_driver fw_ohci_pci_driver = { + .name = ohci_driver_name, + .id_table = pci_table, + .probe = pci_probe, + .remove = pci_remove, +}; + +MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>"); +MODULE_DESCRIPTION("Driver for PCI OHCI IEEE1394 controllers"); +MODULE_LICENSE("GPL"); + +/* Provide a module alias so root-on-sbp2 initrds don't break. */ +#ifndef CONFIG_IEEE1394_OHCI1394_MODULE +MODULE_ALIAS("ohci1394"); +#endif + +static int __init fw_ohci_init(void) +{ + return pci_register_driver(&fw_ohci_pci_driver); +} + +static void __exit fw_ohci_cleanup(void) +{ + pci_unregister_driver(&fw_ohci_pci_driver); +} + +module_init(fw_ohci_init); +module_exit(fw_ohci_cleanup); diff --git a/drivers/firewire/fw-ohci.h b/drivers/firewire/fw-ohci.h new file mode 100644 index 000000000000..fa15706397d7 --- /dev/null +++ b/drivers/firewire/fw-ohci.h @@ -0,0 +1,153 @@ +#ifndef __fw_ohci_h +#define __fw_ohci_h + +/* OHCI register map */ + +#define OHCI1394_Version 0x000 +#define OHCI1394_GUID_ROM 0x004 +#define OHCI1394_ATRetries 0x008 +#define OHCI1394_CSRData 0x00C +#define OHCI1394_CSRCompareData 0x010 +#define OHCI1394_CSRControl 0x014 +#define OHCI1394_ConfigROMhdr 0x018 +#define OHCI1394_BusID 0x01C +#define OHCI1394_BusOptions 0x020 +#define OHCI1394_GUIDHi 0x024 +#define OHCI1394_GUIDLo 0x028 +#define OHCI1394_ConfigROMmap 0x034 +#define OHCI1394_PostedWriteAddressLo 0x038 +#define OHCI1394_PostedWriteAddressHi 0x03C +#define OHCI1394_VendorID 0x040 +#define OHCI1394_HCControlSet 0x050 +#define OHCI1394_HCControlClear 0x054 +#define OHCI1394_HCControl_BIBimageValid 0x80000000 +#define OHCI1394_HCControl_noByteSwapData 0x40000000 +#define OHCI1394_HCControl_programPhyEnable 0x00800000 +#define OHCI1394_HCControl_aPhyEnhanceEnable 0x00400000 +#define OHCI1394_HCControl_LPS 0x00080000 +#define OHCI1394_HCControl_postedWriteEnable 0x00040000 +#define OHCI1394_HCControl_linkEnable 0x00020000 +#define OHCI1394_HCControl_softReset 0x00010000 +#define OHCI1394_SelfIDBuffer 0x064 +#define OHCI1394_SelfIDCount 0x068 +#define OHCI1394_IRMultiChanMaskHiSet 0x070 +#define OHCI1394_IRMultiChanMaskHiClear 0x074 +#define OHCI1394_IRMultiChanMaskLoSet 0x078 +#define OHCI1394_IRMultiChanMaskLoClear 0x07C +#define OHCI1394_IntEventSet 0x080 +#define OHCI1394_IntEventClear 0x084 +#define OHCI1394_IntMaskSet 0x088 +#define OHCI1394_IntMaskClear 0x08C +#define OHCI1394_IsoXmitIntEventSet 0x090 +#define OHCI1394_IsoXmitIntEventClear 0x094 +#define OHCI1394_IsoXmitIntMaskSet 0x098 +#define OHCI1394_IsoXmitIntMaskClear 0x09C +#define OHCI1394_IsoRecvIntEventSet 0x0A0 +#define OHCI1394_IsoRecvIntEventClear 0x0A4 +#define OHCI1394_IsoRecvIntMaskSet 0x0A8 +#define OHCI1394_IsoRecvIntMaskClear 0x0AC +#define OHCI1394_InitialBandwidthAvailable 0x0B0 +#define OHCI1394_InitialChannelsAvailableHi 0x0B4 +#define OHCI1394_InitialChannelsAvailableLo 0x0B8 +#define OHCI1394_FairnessControl 0x0DC +#define OHCI1394_LinkControlSet 0x0E0 +#define OHCI1394_LinkControlClear 0x0E4 +#define OHCI1394_LinkControl_rcvSelfID (1 << 9) +#define OHCI1394_LinkControl_rcvPhyPkt (1 << 10) +#define OHCI1394_LinkControl_cycleTimerEnable (1 << 20) +#define OHCI1394_LinkControl_cycleMaster (1 << 21) +#define OHCI1394_LinkControl_cycleSource (1 << 22) +#define OHCI1394_NodeID 0x0E8 +#define OHCI1394_NodeID_idValid 0x80000000 +#define OHCI1394_PhyControl 0x0EC +#define OHCI1394_PhyControl_Read(addr) (((addr) << 8) | 0x00008000) +#define OHCI1394_PhyControl_ReadDone 0x80000000 +#define OHCI1394_PhyControl_ReadData(r) (((r) & 0x00ff0000) >> 16) +#define OHCI1394_PhyControl_Write(addr, data) (((addr) << 8) | (data) | 0x00004000) +#define OHCI1394_PhyControl_WriteDone 0x00004000 +#define OHCI1394_IsochronousCycleTimer 0x0F0 +#define OHCI1394_AsReqFilterHiSet 0x100 +#define OHCI1394_AsReqFilterHiClear 0x104 +#define OHCI1394_AsReqFilterLoSet 0x108 +#define OHCI1394_AsReqFilterLoClear 0x10C +#define OHCI1394_PhyReqFilterHiSet 0x110 +#define OHCI1394_PhyReqFilterHiClear 0x114 +#define OHCI1394_PhyReqFilterLoSet 0x118 +#define OHCI1394_PhyReqFilterLoClear 0x11C +#define OHCI1394_PhyUpperBound 0x120 + +#define OHCI1394_AsReqTrContextBase 0x180 +#define OHCI1394_AsReqTrContextControlSet 0x180 +#define OHCI1394_AsReqTrContextControlClear 0x184 +#define OHCI1394_AsReqTrCommandPtr 0x18C + +#define OHCI1394_AsRspTrContextBase 0x1A0 +#define OHCI1394_AsRspTrContextControlSet 0x1A0 +#define OHCI1394_AsRspTrContextControlClear 0x1A4 +#define OHCI1394_AsRspTrCommandPtr 0x1AC + +#define OHCI1394_AsReqRcvContextBase 0x1C0 +#define OHCI1394_AsReqRcvContextControlSet 0x1C0 +#define OHCI1394_AsReqRcvContextControlClear 0x1C4 +#define OHCI1394_AsReqRcvCommandPtr 0x1CC + +#define OHCI1394_AsRspRcvContextBase 0x1E0 +#define OHCI1394_AsRspRcvContextControlSet 0x1E0 +#define OHCI1394_AsRspRcvContextControlClear 0x1E4 +#define OHCI1394_AsRspRcvCommandPtr 0x1EC + +/* Isochronous transmit registers */ +#define OHCI1394_IsoXmitContextBase(n) (0x200 + 16 * (n)) +#define OHCI1394_IsoXmitContextControlSet(n) (0x200 + 16 * (n)) +#define OHCI1394_IsoXmitContextControlClear(n) (0x204 + 16 * (n)) +#define OHCI1394_IsoXmitCommandPtr(n) (0x20C + 16 * (n)) + +/* Isochronous receive registers */ +#define OHCI1394_IsoRcvContextBase(n) (0x400 + 32 * (n)) +#define OHCI1394_IsoRcvContextControlSet(n) (0x400 + 32 * (n)) +#define OHCI1394_IsoRcvContextControlClear(n) (0x404 + 32 * (n)) +#define OHCI1394_IsoRcvCommandPtr(n) (0x40C + 32 * (n)) +#define OHCI1394_IsoRcvContextMatch(n) (0x410 + 32 * (n)) + +/* Interrupts Mask/Events */ +#define OHCI1394_reqTxComplete 0x00000001 +#define OHCI1394_respTxComplete 0x00000002 +#define OHCI1394_ARRQ 0x00000004 +#define OHCI1394_ARRS 0x00000008 +#define OHCI1394_RQPkt 0x00000010 +#define OHCI1394_RSPkt 0x00000020 +#define OHCI1394_isochTx 0x00000040 +#define OHCI1394_isochRx 0x00000080 +#define OHCI1394_postedWriteErr 0x00000100 +#define OHCI1394_lockRespErr 0x00000200 +#define OHCI1394_selfIDComplete 0x00010000 +#define OHCI1394_busReset 0x00020000 +#define OHCI1394_phy 0x00080000 +#define OHCI1394_cycleSynch 0x00100000 +#define OHCI1394_cycle64Seconds 0x00200000 +#define OHCI1394_cycleLost 0x00400000 +#define OHCI1394_cycleInconsistent 0x00800000 +#define OHCI1394_unrecoverableError 0x01000000 +#define OHCI1394_cycleTooLong 0x02000000 +#define OHCI1394_phyRegRcvd 0x04000000 +#define OHCI1394_masterIntEnable 0x80000000 + +#define OHCI1394_evt_no_status 0x0 +#define OHCI1394_evt_long_packet 0x2 +#define OHCI1394_evt_missing_ack 0x3 +#define OHCI1394_evt_underrun 0x4 +#define OHCI1394_evt_overrun 0x5 +#define OHCI1394_evt_descriptor_read 0x6 +#define OHCI1394_evt_data_read 0x7 +#define OHCI1394_evt_data_write 0x8 +#define OHCI1394_evt_bus_reset 0x9 +#define OHCI1394_evt_timeout 0xa +#define OHCI1394_evt_tcode_err 0xb +#define OHCI1394_evt_reserved_b 0xc +#define OHCI1394_evt_reserved_c 0xd +#define OHCI1394_evt_unknown 0xe +#define OHCI1394_evt_flushed 0xf + +#define OHCI1394_phy_tcode 0xe + +#endif /* __fw_ohci_h */ diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c new file mode 100644 index 000000000000..68300414e5f4 --- /dev/null +++ b/drivers/firewire/fw-sbp2.c @@ -0,0 +1,1147 @@ +/* + * SBP2 driver (SCSI over IEEE1394) + * + * Copyright (C) 2005-2007 Kristian Hoegsberg <krh@bitplanet.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * The basic structure of this driver is based on the old storage driver, + * drivers/ieee1394/sbp2.c, originally written by + * James Goodwin <jamesg@filanet.com> + * with later contributions and ongoing maintenance from + * Ben Collins <bcollins@debian.org>, + * Stefan Richter <stefanr@s5r6.in-berlin.de> + * and many others. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/device.h> +#include <linux/scatterlist.h> +#include <linux/dma-mapping.h> +#include <linux/timer.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_dbg.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> + +#include "fw-transaction.h" +#include "fw-topology.h" +#include "fw-device.h" + +/* I don't know why the SCSI stack doesn't define something like this... */ +typedef void (*scsi_done_fn_t)(struct scsi_cmnd *); + +static const char sbp2_driver_name[] = "sbp2"; + +struct sbp2_device { + struct kref kref; + struct fw_unit *unit; + struct fw_address_handler address_handler; + struct list_head orb_list; + u64 management_agent_address; + u64 command_block_agent_address; + u32 workarounds; + int login_id; + + /* + * We cache these addresses and only update them once we've + * logged in or reconnected to the sbp2 device. That way, any + * IO to the device will automatically fail and get retried if + * it happens in a window where the device is not ready to + * handle it (e.g. after a bus reset but before we reconnect). + */ + int node_id; + int address_high; + int generation; + + int retries; + struct delayed_work work; +}; + +#define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000 +#define SBP2_MAX_SECTORS 255 /* Max sectors supported */ +#define SBP2_ORB_TIMEOUT 2000 /* Timeout in ms */ + +#define SBP2_ORB_NULL 0x80000000 + +#define SBP2_DIRECTION_TO_MEDIA 0x0 +#define SBP2_DIRECTION_FROM_MEDIA 0x1 + +/* Unit directory keys */ +#define SBP2_COMMAND_SET_SPECIFIER 0x38 +#define SBP2_COMMAND_SET 0x39 +#define SBP2_COMMAND_SET_REVISION 0x3b +#define SBP2_FIRMWARE_REVISION 0x3c + +/* Flags for detected oddities and brokeness */ +#define SBP2_WORKAROUND_128K_MAX_TRANS 0x1 +#define SBP2_WORKAROUND_INQUIRY_36 0x2 +#define SBP2_WORKAROUND_MODE_SENSE_8 0x4 +#define SBP2_WORKAROUND_FIX_CAPACITY 0x8 +#define SBP2_WORKAROUND_OVERRIDE 0x100 + +/* Management orb opcodes */ +#define SBP2_LOGIN_REQUEST 0x0 +#define SBP2_QUERY_LOGINS_REQUEST 0x1 +#define SBP2_RECONNECT_REQUEST 0x3 +#define SBP2_SET_PASSWORD_REQUEST 0x4 +#define SBP2_LOGOUT_REQUEST 0x7 +#define SBP2_ABORT_TASK_REQUEST 0xb +#define SBP2_ABORT_TASK_SET 0xc +#define SBP2_LOGICAL_UNIT_RESET 0xe +#define SBP2_TARGET_RESET_REQUEST 0xf + +/* Offsets for command block agent registers */ +#define SBP2_AGENT_STATE 0x00 +#define SBP2_AGENT_RESET 0x04 +#define SBP2_ORB_POINTER 0x08 +#define SBP2_DOORBELL 0x10 +#define SBP2_UNSOLICITED_STATUS_ENABLE 0x14 + +/* Status write response codes */ +#define SBP2_STATUS_REQUEST_COMPLETE 0x0 +#define SBP2_STATUS_TRANSPORT_FAILURE 0x1 +#define SBP2_STATUS_ILLEGAL_REQUEST 0x2 +#define SBP2_STATUS_VENDOR_DEPENDENT 0x3 + +#define STATUS_GET_ORB_HIGH(v) ((v).status & 0xffff) +#define STATUS_GET_SBP_STATUS(v) (((v).status >> 16) & 0xff) +#define STATUS_GET_LEN(v) (((v).status >> 24) & 0x07) +#define STATUS_GET_DEAD(v) (((v).status >> 27) & 0x01) +#define STATUS_GET_RESPONSE(v) (((v).status >> 28) & 0x03) +#define STATUS_GET_SOURCE(v) (((v).status >> 30) & 0x03) +#define STATUS_GET_ORB_LOW(v) ((v).orb_low) +#define STATUS_GET_DATA(v) ((v).data) + +struct sbp2_status { + u32 status; + u32 orb_low; + u8 data[24]; +}; + +struct sbp2_pointer { + u32 high; + u32 low; +}; + +struct sbp2_orb { + struct fw_transaction t; + dma_addr_t request_bus; + int rcode; + struct sbp2_pointer pointer; + void (*callback)(struct sbp2_orb * orb, struct sbp2_status * status); + struct list_head link; +}; + +#define MANAGEMENT_ORB_LUN(v) ((v)) +#define MANAGEMENT_ORB_FUNCTION(v) ((v) << 16) +#define MANAGEMENT_ORB_RECONNECT(v) ((v) << 20) +#define MANAGEMENT_ORB_EXCLUSIVE ((1) << 28) +#define MANAGEMENT_ORB_REQUEST_FORMAT(v) ((v) << 29) +#define MANAGEMENT_ORB_NOTIFY ((1) << 31) + +#define MANAGEMENT_ORB_RESPONSE_LENGTH(v) ((v)) +#define MANAGEMENT_ORB_PASSWORD_LENGTH(v) ((v) << 16) + +struct sbp2_management_orb { + struct sbp2_orb base; + struct { + struct sbp2_pointer password; + struct sbp2_pointer response; + u32 misc; + u32 length; + struct sbp2_pointer status_fifo; + } request; + __be32 response[4]; + dma_addr_t response_bus; + struct completion done; + struct sbp2_status status; +}; + +#define LOGIN_RESPONSE_GET_LOGIN_ID(v) ((v).misc & 0xffff) +#define LOGIN_RESPONSE_GET_LENGTH(v) (((v).misc >> 16) & 0xffff) + +struct sbp2_login_response { + u32 misc; + struct sbp2_pointer command_block_agent; + u32 reconnect_hold; +}; +#define COMMAND_ORB_DATA_SIZE(v) ((v)) +#define COMMAND_ORB_PAGE_SIZE(v) ((v) << 16) +#define COMMAND_ORB_PAGE_TABLE_PRESENT ((1) << 19) +#define COMMAND_ORB_MAX_PAYLOAD(v) ((v) << 20) +#define COMMAND_ORB_SPEED(v) ((v) << 24) +#define COMMAND_ORB_DIRECTION(v) ((v) << 27) +#define COMMAND_ORB_REQUEST_FORMAT(v) ((v) << 29) +#define COMMAND_ORB_NOTIFY ((1) << 31) + +struct sbp2_command_orb { + struct sbp2_orb base; + struct { + struct sbp2_pointer next; + struct sbp2_pointer data_descriptor; + u32 misc; + u8 command_block[12]; + } request; + struct scsi_cmnd *cmd; + scsi_done_fn_t done; + struct fw_unit *unit; + + struct sbp2_pointer page_table[SG_ALL]; + dma_addr_t page_table_bus; + dma_addr_t request_buffer_bus; +}; + +/* + * List of devices with known bugs. + * + * The firmware_revision field, masked with 0xffff00, is the best + * indicator for the type of bridge chip of a device. It yields a few + * false positives but this did not break correctly behaving devices + * so far. We use ~0 as a wildcard, since the 24 bit values we get + * from the config rom can never match that. + */ +static const struct { + u32 firmware_revision; + u32 model; + unsigned workarounds; +} sbp2_workarounds_table[] = { + /* DViCO Momobay CX-1 with TSB42AA9 bridge */ { + .firmware_revision = 0x002800, + .model = 0x001010, + .workarounds = SBP2_WORKAROUND_INQUIRY_36 | + SBP2_WORKAROUND_MODE_SENSE_8, + }, + /* Initio bridges, actually only needed for some older ones */ { + .firmware_revision = 0x000200, + .model = ~0, + .workarounds = SBP2_WORKAROUND_INQUIRY_36, + }, + /* Symbios bridge */ { + .firmware_revision = 0xa0b800, + .model = ~0, + .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, + }, + + /* + * There are iPods (2nd gen, 3rd gen) with model_id == 0, but + * these iPods do not feature the read_capacity bug according + * to one report. Read_capacity behaviour as well as model_id + * could change due to Apple-supplied firmware updates though. + */ + + /* iPod 4th generation. */ { + .firmware_revision = 0x0a2700, + .model = 0x000021, + .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, + }, + /* iPod mini */ { + .firmware_revision = 0x0a2700, + .model = 0x000023, + .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, + }, + /* iPod Photo */ { + .firmware_revision = 0x0a2700, + .model = 0x00007e, + .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, + } +}; + +static void +sbp2_status_write(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, int speed, + unsigned long long offset, + void *payload, size_t length, void *callback_data) +{ + struct sbp2_device *sd = callback_data; + struct sbp2_orb *orb; + struct sbp2_status status; + size_t header_size; + unsigned long flags; + + if (tcode != TCODE_WRITE_BLOCK_REQUEST || + length == 0 || length > sizeof(status)) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + + header_size = min(length, 2 * sizeof(u32)); + fw_memcpy_from_be32(&status, payload, header_size); + if (length > header_size) + memcpy(status.data, payload + 8, length - header_size); + if (STATUS_GET_SOURCE(status) == 2 || STATUS_GET_SOURCE(status) == 3) { + fw_notify("non-orb related status write, not handled\n"); + fw_send_response(card, request, RCODE_COMPLETE); + return; + } + + /* Lookup the orb corresponding to this status write. */ + spin_lock_irqsave(&card->lock, flags); + list_for_each_entry(orb, &sd->orb_list, link) { + if (STATUS_GET_ORB_HIGH(status) == 0 && + STATUS_GET_ORB_LOW(status) == orb->request_bus && + orb->rcode == RCODE_COMPLETE) { + list_del(&orb->link); + break; + } + } + spin_unlock_irqrestore(&card->lock, flags); + + if (&orb->link != &sd->orb_list) + orb->callback(orb, &status); + else + fw_error("status write for unknown orb\n"); + + fw_send_response(card, request, RCODE_COMPLETE); +} + +static void +complete_transaction(struct fw_card *card, int rcode, + void *payload, size_t length, void *data) +{ + struct sbp2_orb *orb = data; + unsigned long flags; + + orb->rcode = rcode; + if (rcode != RCODE_COMPLETE) { + spin_lock_irqsave(&card->lock, flags); + list_del(&orb->link); + spin_unlock_irqrestore(&card->lock, flags); + orb->callback(orb, NULL); + } +} + +static void +sbp2_send_orb(struct sbp2_orb *orb, struct fw_unit *unit, + int node_id, int generation, u64 offset) +{ + struct fw_device *device = fw_device(unit->device.parent); + struct sbp2_device *sd = unit->device.driver_data; + unsigned long flags; + + orb->pointer.high = 0; + orb->pointer.low = orb->request_bus; + fw_memcpy_to_be32(&orb->pointer, &orb->pointer, sizeof(orb->pointer)); + + spin_lock_irqsave(&device->card->lock, flags); + list_add_tail(&orb->link, &sd->orb_list); + spin_unlock_irqrestore(&device->card->lock, flags); + + fw_send_request(device->card, &orb->t, TCODE_WRITE_BLOCK_REQUEST, + node_id, generation, + device->node->max_speed, offset, + &orb->pointer, sizeof(orb->pointer), + complete_transaction, orb); +} + +static int sbp2_cancel_orbs(struct fw_unit *unit) +{ + struct fw_device *device = fw_device(unit->device.parent); + struct sbp2_device *sd = unit->device.driver_data; + struct sbp2_orb *orb, *next; + struct list_head list; + unsigned long flags; + int retval = -ENOENT; + + INIT_LIST_HEAD(&list); + spin_lock_irqsave(&device->card->lock, flags); + list_splice_init(&sd->orb_list, &list); + spin_unlock_irqrestore(&device->card->lock, flags); + + list_for_each_entry_safe(orb, next, &list, link) { + retval = 0; + if (fw_cancel_transaction(device->card, &orb->t) == 0) + continue; + + orb->rcode = RCODE_CANCELLED; + orb->callback(orb, NULL); + } + + return retval; +} + +static void +complete_management_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) +{ + struct sbp2_management_orb *orb = + (struct sbp2_management_orb *)base_orb; + + if (status) + memcpy(&orb->status, status, sizeof(*status)); + complete(&orb->done); +} + +static int +sbp2_send_management_orb(struct fw_unit *unit, int node_id, int generation, + int function, int lun, void *response) +{ + struct fw_device *device = fw_device(unit->device.parent); + struct sbp2_device *sd = unit->device.driver_data; + struct sbp2_management_orb *orb; + int retval = -ENOMEM; + + orb = kzalloc(sizeof(*orb), GFP_ATOMIC); + if (orb == NULL) + return -ENOMEM; + + /* + * The sbp2 device is going to send a block read request to + * read out the request from host memory, so map it for dma. + */ + orb->base.request_bus = + dma_map_single(device->card->device, &orb->request, + sizeof(orb->request), DMA_TO_DEVICE); + if (dma_mapping_error(orb->base.request_bus)) + goto out; + + orb->response_bus = + dma_map_single(device->card->device, &orb->response, + sizeof(orb->response), DMA_FROM_DEVICE); + if (dma_mapping_error(orb->response_bus)) + goto out; + + orb->request.response.high = 0; + orb->request.response.low = orb->response_bus; + + orb->request.misc = + MANAGEMENT_ORB_NOTIFY | + MANAGEMENT_ORB_FUNCTION(function) | + MANAGEMENT_ORB_LUN(lun); + orb->request.length = + MANAGEMENT_ORB_RESPONSE_LENGTH(sizeof(orb->response)); + + orb->request.status_fifo.high = sd->address_handler.offset >> 32; + orb->request.status_fifo.low = sd->address_handler.offset; + + /* + * FIXME: Yeah, ok this isn't elegant, we hardwire exclusive + * login and 1 second reconnect time. The reconnect setting + * is probably fine, but the exclusive login should be an option. + */ + if (function == SBP2_LOGIN_REQUEST) { + orb->request.misc |= + MANAGEMENT_ORB_EXCLUSIVE | + MANAGEMENT_ORB_RECONNECT(0); + } + + fw_memcpy_to_be32(&orb->request, &orb->request, sizeof(orb->request)); + + init_completion(&orb->done); + orb->base.callback = complete_management_orb; + + sbp2_send_orb(&orb->base, unit, + node_id, generation, sd->management_agent_address); + + wait_for_completion_timeout(&orb->done, + msecs_to_jiffies(SBP2_ORB_TIMEOUT)); + + retval = -EIO; + if (sbp2_cancel_orbs(unit) == 0) { + fw_error("orb reply timed out, rcode=0x%02x\n", + orb->base.rcode); + goto out; + } + + if (orb->base.rcode != RCODE_COMPLETE) { + fw_error("management write failed, rcode 0x%02x\n", + orb->base.rcode); + goto out; + } + + if (STATUS_GET_RESPONSE(orb->status) != 0 || + STATUS_GET_SBP_STATUS(orb->status) != 0) { + fw_error("error status: %d:%d\n", + STATUS_GET_RESPONSE(orb->status), + STATUS_GET_SBP_STATUS(orb->status)); + goto out; + } + + retval = 0; + out: + dma_unmap_single(device->card->device, orb->base.request_bus, + sizeof(orb->request), DMA_TO_DEVICE); + dma_unmap_single(device->card->device, orb->response_bus, + sizeof(orb->response), DMA_FROM_DEVICE); + + if (response) + fw_memcpy_from_be32(response, + orb->response, sizeof(orb->response)); + kfree(orb); + + return retval; +} + +static void +complete_agent_reset_write(struct fw_card *card, int rcode, + void *payload, size_t length, void *data) +{ + struct fw_transaction *t = data; + + kfree(t); +} + +static int sbp2_agent_reset(struct fw_unit *unit) +{ + struct fw_device *device = fw_device(unit->device.parent); + struct sbp2_device *sd = unit->device.driver_data; + struct fw_transaction *t; + static u32 zero; + + t = kzalloc(sizeof(*t), GFP_ATOMIC); + if (t == NULL) + return -ENOMEM; + + fw_send_request(device->card, t, TCODE_WRITE_QUADLET_REQUEST, + sd->node_id, sd->generation, SCODE_400, + sd->command_block_agent_address + SBP2_AGENT_RESET, + &zero, sizeof(zero), complete_agent_reset_write, t); + + return 0; +} + +static void sbp2_reconnect(struct work_struct *work); +static struct scsi_host_template scsi_driver_template; + +static void +release_sbp2_device(struct kref *kref) +{ + struct sbp2_device *sd = container_of(kref, struct sbp2_device, kref); + struct Scsi_Host *host = + container_of((void *)sd, struct Scsi_Host, hostdata[0]); + + sbp2_send_management_orb(sd->unit, sd->node_id, sd->generation, + SBP2_LOGOUT_REQUEST, sd->login_id, NULL); + + scsi_remove_host(host); + fw_core_remove_address_handler(&sd->address_handler); + fw_notify("removed sbp2 unit %s\n", sd->unit->device.bus_id); + put_device(&sd->unit->device); + scsi_host_put(host); +} + +static void sbp2_login(struct work_struct *work) +{ + struct sbp2_device *sd = + container_of(work, struct sbp2_device, work.work); + struct Scsi_Host *host = + container_of((void *)sd, struct Scsi_Host, hostdata[0]); + struct fw_unit *unit = sd->unit; + struct fw_device *device = fw_device(unit->device.parent); + struct sbp2_login_response response; + int generation, node_id, local_node_id, lun, retval; + + /* FIXME: Make this work for multi-lun devices. */ + lun = 0; + + generation = device->card->generation; + node_id = device->node->node_id; + local_node_id = device->card->local_node->node_id; + + if (sbp2_send_management_orb(unit, node_id, generation, + SBP2_LOGIN_REQUEST, lun, &response) < 0) { + if (sd->retries++ < 5) { + schedule_delayed_work(&sd->work, DIV_ROUND_UP(HZ, 5)); + } else { + fw_error("failed to login to %s\n", + unit->device.bus_id); + kref_put(&sd->kref, release_sbp2_device); + } + return; + } + + sd->generation = generation; + sd->node_id = node_id; + sd->address_high = local_node_id << 16; + + /* Get command block agent offset and login id. */ + sd->command_block_agent_address = + ((u64) (response.command_block_agent.high & 0xffff) << 32) | + response.command_block_agent.low; + sd->login_id = LOGIN_RESPONSE_GET_LOGIN_ID(response); + + fw_notify("logged in to sbp2 unit %s (%d retries)\n", + unit->device.bus_id, sd->retries); + fw_notify(" - management_agent_address: 0x%012llx\n", + (unsigned long long) sd->management_agent_address); + fw_notify(" - command_block_agent_address: 0x%012llx\n", + (unsigned long long) sd->command_block_agent_address); + fw_notify(" - status write address: 0x%012llx\n", + (unsigned long long) sd->address_handler.offset); + +#if 0 + /* FIXME: The linux1394 sbp2 does this last step. */ + sbp2_set_busy_timeout(scsi_id); +#endif + + PREPARE_DELAYED_WORK(&sd->work, sbp2_reconnect); + sbp2_agent_reset(unit); + + /* FIXME: Loop over luns here. */ + lun = 0; + retval = scsi_add_device(host, 0, 0, lun); + if (retval < 0) { + sbp2_send_management_orb(unit, sd->node_id, sd->generation, + SBP2_LOGOUT_REQUEST, sd->login_id, + NULL); + /* + * Set this back to sbp2_login so we fall back and + * retry login on bus reset. + */ + PREPARE_DELAYED_WORK(&sd->work, sbp2_login); + } + kref_put(&sd->kref, release_sbp2_device); +} + +static int sbp2_probe(struct device *dev) +{ + struct fw_unit *unit = fw_unit(dev); + struct fw_device *device = fw_device(unit->device.parent); + struct sbp2_device *sd; + struct fw_csr_iterator ci; + struct Scsi_Host *host; + int i, key, value, err; + u32 model, firmware_revision; + + err = -ENOMEM; + host = scsi_host_alloc(&scsi_driver_template, sizeof(*sd)); + if (host == NULL) + goto fail; + + sd = (struct sbp2_device *) host->hostdata; + unit->device.driver_data = sd; + sd->unit = unit; + INIT_LIST_HEAD(&sd->orb_list); + kref_init(&sd->kref); + + sd->address_handler.length = 0x100; + sd->address_handler.address_callback = sbp2_status_write; + sd->address_handler.callback_data = sd; + + err = fw_core_add_address_handler(&sd->address_handler, + &fw_high_memory_region); + if (err < 0) + goto fail_host; + + err = fw_device_enable_phys_dma(device); + if (err < 0) + goto fail_address_handler; + + err = scsi_add_host(host, &unit->device); + if (err < 0) + goto fail_address_handler; + + /* + * Scan unit directory to get management agent address, + * firmware revison and model. Initialize firmware_revision + * and model to values that wont match anything in our table. + */ + firmware_revision = 0xff000000; + model = 0xff000000; + fw_csr_iterator_init(&ci, unit->directory); + while (fw_csr_iterator_next(&ci, &key, &value)) { + switch (key) { + case CSR_DEPENDENT_INFO | CSR_OFFSET: + sd->management_agent_address = + 0xfffff0000000ULL + 4 * value; + break; + case SBP2_FIRMWARE_REVISION: + firmware_revision = value; + break; + case CSR_MODEL: + model = value; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) { + if (sbp2_workarounds_table[i].firmware_revision != + (firmware_revision & 0xffffff00)) + continue; + if (sbp2_workarounds_table[i].model != model && + sbp2_workarounds_table[i].model != ~0) + continue; + sd->workarounds |= sbp2_workarounds_table[i].workarounds; + break; + } + + if (sd->workarounds) + fw_notify("Workarounds for node %s: 0x%x " + "(firmware_revision 0x%06x, model_id 0x%06x)\n", + unit->device.bus_id, + sd->workarounds, firmware_revision, model); + + get_device(&unit->device); + + /* + * We schedule work to do the login so we can easily + * reschedule retries. Always get the ref before scheduling + * work. + */ + INIT_DELAYED_WORK(&sd->work, sbp2_login); + if (schedule_delayed_work(&sd->work, 0)) + kref_get(&sd->kref); + + return 0; + + fail_address_handler: + fw_core_remove_address_handler(&sd->address_handler); + fail_host: + scsi_host_put(host); + fail: + return err; +} + +static int sbp2_remove(struct device *dev) +{ + struct fw_unit *unit = fw_unit(dev); + struct sbp2_device *sd = unit->device.driver_data; + + kref_put(&sd->kref, release_sbp2_device); + + return 0; +} + +static void sbp2_reconnect(struct work_struct *work) +{ + struct sbp2_device *sd = + container_of(work, struct sbp2_device, work.work); + struct fw_unit *unit = sd->unit; + struct fw_device *device = fw_device(unit->device.parent); + int generation, node_id, local_node_id; + + generation = device->card->generation; + node_id = device->node->node_id; + local_node_id = device->card->local_node->node_id; + + if (sbp2_send_management_orb(unit, node_id, generation, + SBP2_RECONNECT_REQUEST, + sd->login_id, NULL) < 0) { + if (sd->retries++ >= 5) { + fw_error("failed to reconnect to %s\n", + unit->device.bus_id); + /* Fall back and try to log in again. */ + sd->retries = 0; + PREPARE_DELAYED_WORK(&sd->work, sbp2_login); + } + schedule_delayed_work(&sd->work, DIV_ROUND_UP(HZ, 5)); + return; + } + + sd->generation = generation; + sd->node_id = node_id; + sd->address_high = local_node_id << 16; + + fw_notify("reconnected to unit %s (%d retries)\n", + unit->device.bus_id, sd->retries); + sbp2_agent_reset(unit); + sbp2_cancel_orbs(unit); + kref_put(&sd->kref, release_sbp2_device); +} + +static void sbp2_update(struct fw_unit *unit) +{ + struct fw_device *device = fw_device(unit->device.parent); + struct sbp2_device *sd = unit->device.driver_data; + + sd->retries = 0; + fw_device_enable_phys_dma(device); + if (schedule_delayed_work(&sd->work, 0)) + kref_get(&sd->kref); +} + +#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e +#define SBP2_SW_VERSION_ENTRY 0x00010483 + +static const struct fw_device_id sbp2_id_table[] = { + { + .match_flags = FW_MATCH_SPECIFIER_ID | FW_MATCH_VERSION, + .specifier_id = SBP2_UNIT_SPEC_ID_ENTRY, + .version = SBP2_SW_VERSION_ENTRY, + }, + { } +}; + +static struct fw_driver sbp2_driver = { + .driver = { + .owner = THIS_MODULE, + .name = sbp2_driver_name, + .bus = &fw_bus_type, + .probe = sbp2_probe, + .remove = sbp2_remove, + }, + .update = sbp2_update, + .id_table = sbp2_id_table, +}; + +static unsigned int +sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data) +{ + int sam_status; + + sense_data[0] = 0x70; + sense_data[1] = 0x0; + sense_data[2] = sbp2_status[1]; + sense_data[3] = sbp2_status[4]; + sense_data[4] = sbp2_status[5]; + sense_data[5] = sbp2_status[6]; + sense_data[6] = sbp2_status[7]; + sense_data[7] = 10; + sense_data[8] = sbp2_status[8]; + sense_data[9] = sbp2_status[9]; + sense_data[10] = sbp2_status[10]; + sense_data[11] = sbp2_status[11]; + sense_data[12] = sbp2_status[2]; + sense_data[13] = sbp2_status[3]; + sense_data[14] = sbp2_status[12]; + sense_data[15] = sbp2_status[13]; + + sam_status = sbp2_status[0] & 0x3f; + + switch (sam_status) { + case SAM_STAT_GOOD: + case SAM_STAT_CHECK_CONDITION: + case SAM_STAT_CONDITION_MET: + case SAM_STAT_BUSY: + case SAM_STAT_RESERVATION_CONFLICT: + case SAM_STAT_COMMAND_TERMINATED: + return DID_OK << 16 | sam_status; + + default: + return DID_ERROR << 16; + } +} + +static void +complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) +{ + struct sbp2_command_orb *orb = (struct sbp2_command_orb *)base_orb; + struct fw_unit *unit = orb->unit; + struct fw_device *device = fw_device(unit->device.parent); + struct scatterlist *sg; + int result; + + if (status != NULL) { + if (STATUS_GET_DEAD(*status)) + sbp2_agent_reset(unit); + + switch (STATUS_GET_RESPONSE(*status)) { + case SBP2_STATUS_REQUEST_COMPLETE: + result = DID_OK << 16; + break; + case SBP2_STATUS_TRANSPORT_FAILURE: + result = DID_BUS_BUSY << 16; + break; + case SBP2_STATUS_ILLEGAL_REQUEST: + case SBP2_STATUS_VENDOR_DEPENDENT: + default: + result = DID_ERROR << 16; + break; + } + + if (result == DID_OK << 16 && STATUS_GET_LEN(*status) > 1) + result = sbp2_status_to_sense_data(STATUS_GET_DATA(*status), + orb->cmd->sense_buffer); + } else { + /* + * If the orb completes with status == NULL, something + * went wrong, typically a bus reset happened mid-orb + * or when sending the write (less likely). + */ + result = DID_BUS_BUSY << 16; + } + + dma_unmap_single(device->card->device, orb->base.request_bus, + sizeof(orb->request), DMA_TO_DEVICE); + + if (orb->cmd->use_sg > 0) { + sg = (struct scatterlist *)orb->cmd->request_buffer; + dma_unmap_sg(device->card->device, sg, orb->cmd->use_sg, + orb->cmd->sc_data_direction); + } + + if (orb->page_table_bus != 0) + dma_unmap_single(device->card->device, orb->page_table_bus, + sizeof(orb->page_table_bus), DMA_TO_DEVICE); + + if (orb->request_buffer_bus != 0) + dma_unmap_single(device->card->device, orb->request_buffer_bus, + sizeof(orb->request_buffer_bus), + DMA_FROM_DEVICE); + + orb->cmd->result = result; + orb->done(orb->cmd); + kfree(orb); +} + +static int sbp2_command_orb_map_scatterlist(struct sbp2_command_orb *orb) +{ + struct sbp2_device *sd = + (struct sbp2_device *)orb->cmd->device->host->hostdata; + struct fw_unit *unit = sd->unit; + struct fw_device *device = fw_device(unit->device.parent); + struct scatterlist *sg; + int sg_len, l, i, j, count; + size_t size; + dma_addr_t sg_addr; + + sg = (struct scatterlist *)orb->cmd->request_buffer; + count = dma_map_sg(device->card->device, sg, orb->cmd->use_sg, + orb->cmd->sc_data_direction); + if (count == 0) + goto fail; + + /* + * Handle the special case where there is only one element in + * the scatter list by converting it to an immediate block + * request. This is also a workaround for broken devices such + * as the second generation iPod which doesn't support page + * tables. + */ + if (count == 1 && sg_dma_len(sg) < SBP2_MAX_SG_ELEMENT_LENGTH) { + orb->request.data_descriptor.high = sd->address_high; + orb->request.data_descriptor.low = sg_dma_address(sg); + orb->request.misc |= + COMMAND_ORB_DATA_SIZE(sg_dma_len(sg)); + return 0; + } + + /* + * Convert the scatterlist to an sbp2 page table. If any + * scatterlist entries are too big for sbp2, we split them as we + * go. Even if we ask the block I/O layer to not give us sg + * elements larger than 65535 bytes, some IOMMUs may merge sg elements + * during DMA mapping, and Linux currently doesn't prevent this. + */ + for (i = 0, j = 0; i < count; i++) { + sg_len = sg_dma_len(sg + i); + sg_addr = sg_dma_address(sg + i); + while (sg_len) { + l = min(sg_len, SBP2_MAX_SG_ELEMENT_LENGTH); + orb->page_table[j].low = sg_addr; + orb->page_table[j].high = (l << 16); + sg_addr += l; + sg_len -= l; + j++; + } + } + + size = sizeof(orb->page_table[0]) * j; + + /* + * The data_descriptor pointer is the one case where we need + * to fill in the node ID part of the address. All other + * pointers assume that the data referenced reside on the + * initiator (i.e. us), but data_descriptor can refer to data + * on other nodes so we need to put our ID in descriptor.high. + */ + + orb->page_table_bus = + dma_map_single(device->card->device, orb->page_table, + size, DMA_TO_DEVICE); + if (dma_mapping_error(orb->page_table_bus)) + goto fail_page_table; + orb->request.data_descriptor.high = sd->address_high; + orb->request.data_descriptor.low = orb->page_table_bus; + orb->request.misc |= + COMMAND_ORB_PAGE_TABLE_PRESENT | + COMMAND_ORB_DATA_SIZE(j); + + fw_memcpy_to_be32(orb->page_table, orb->page_table, size); + + return 0; + + fail_page_table: + dma_unmap_sg(device->card->device, sg, orb->cmd->use_sg, + orb->cmd->sc_data_direction); + fail: + return -ENOMEM; +} + +/* SCSI stack integration */ + +static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) +{ + struct sbp2_device *sd = + (struct sbp2_device *)cmd->device->host->hostdata; + struct fw_unit *unit = sd->unit; + struct fw_device *device = fw_device(unit->device.parent); + struct sbp2_command_orb *orb; + + /* + * Bidirectional commands are not yet implemented, and unknown + * transfer direction not handled. + */ + if (cmd->sc_data_direction == DMA_BIDIRECTIONAL) { + fw_error("Cannot handle DMA_BIDIRECTIONAL - rejecting command"); + cmd->result = DID_ERROR << 16; + done(cmd); + return 0; + } + + orb = kzalloc(sizeof(*orb), GFP_ATOMIC); + if (orb == NULL) { + fw_notify("failed to alloc orb\n"); + goto fail_alloc; + } + + /* Initialize rcode to something not RCODE_COMPLETE. */ + orb->base.rcode = -1; + orb->base.request_bus = + dma_map_single(device->card->device, &orb->request, + sizeof(orb->request), DMA_TO_DEVICE); + if (dma_mapping_error(orb->base.request_bus)) + goto fail_mapping; + + orb->unit = unit; + orb->done = done; + orb->cmd = cmd; + + orb->request.next.high = SBP2_ORB_NULL; + orb->request.next.low = 0x0; + /* + * At speed 100 we can do 512 bytes per packet, at speed 200, + * 1024 bytes per packet etc. The SBP-2 max_payload field + * specifies the max payload size as 2 ^ (max_payload + 2), so + * if we set this to max_speed + 7, we get the right value. + */ + orb->request.misc = + COMMAND_ORB_MAX_PAYLOAD(device->node->max_speed + 7) | + COMMAND_ORB_SPEED(device->node->max_speed) | + COMMAND_ORB_NOTIFY; + + if (cmd->sc_data_direction == DMA_FROM_DEVICE) + orb->request.misc |= + COMMAND_ORB_DIRECTION(SBP2_DIRECTION_FROM_MEDIA); + else if (cmd->sc_data_direction == DMA_TO_DEVICE) + orb->request.misc |= + COMMAND_ORB_DIRECTION(SBP2_DIRECTION_TO_MEDIA); + + if (cmd->use_sg && sbp2_command_orb_map_scatterlist(orb) < 0) + goto fail_map_payload; + + fw_memcpy_to_be32(&orb->request, &orb->request, sizeof(orb->request)); + + memset(orb->request.command_block, + 0, sizeof(orb->request.command_block)); + memcpy(orb->request.command_block, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd)); + + orb->base.callback = complete_command_orb; + + sbp2_send_orb(&orb->base, unit, sd->node_id, sd->generation, + sd->command_block_agent_address + SBP2_ORB_POINTER); + + return 0; + + fail_map_payload: + dma_unmap_single(device->card->device, orb->base.request_bus, + sizeof(orb->request), DMA_TO_DEVICE); + fail_mapping: + kfree(orb); + fail_alloc: + return SCSI_MLQUEUE_HOST_BUSY; +} + +static int sbp2_scsi_slave_alloc(struct scsi_device *sdev) +{ + struct sbp2_device *sd = (struct sbp2_device *)sdev->host->hostdata; + + sdev->allow_restart = 1; + + if (sd->workarounds & SBP2_WORKAROUND_INQUIRY_36) + sdev->inquiry_len = 36; + return 0; +} + +static int sbp2_scsi_slave_configure(struct scsi_device *sdev) +{ + struct sbp2_device *sd = (struct sbp2_device *)sdev->host->hostdata; + struct fw_unit *unit = sd->unit; + + sdev->use_10_for_rw = 1; + + if (sdev->type == TYPE_ROM) + sdev->use_10_for_ms = 1; + if (sdev->type == TYPE_DISK && + sd->workarounds & SBP2_WORKAROUND_MODE_SENSE_8) + sdev->skip_ms_page_8 = 1; + if (sd->workarounds & SBP2_WORKAROUND_FIX_CAPACITY) { + fw_notify("setting fix_capacity for %s\n", unit->device.bus_id); + sdev->fix_capacity = 1; + } + + return 0; +} + +/* + * Called by scsi stack when something has really gone wrong. Usually + * called when a command has timed-out for some reason. + */ +static int sbp2_scsi_abort(struct scsi_cmnd *cmd) +{ + struct sbp2_device *sd = + (struct sbp2_device *)cmd->device->host->hostdata; + struct fw_unit *unit = sd->unit; + + fw_notify("sbp2_scsi_abort\n"); + sbp2_agent_reset(unit); + sbp2_cancel_orbs(unit); + + return SUCCESS; +} + +static struct scsi_host_template scsi_driver_template = { + .module = THIS_MODULE, + .name = "SBP-2 IEEE-1394", + .proc_name = (char *)sbp2_driver_name, + .queuecommand = sbp2_scsi_queuecommand, + .slave_alloc = sbp2_scsi_slave_alloc, + .slave_configure = sbp2_scsi_slave_configure, + .eh_abort_handler = sbp2_scsi_abort, + .this_id = -1, + .sg_tablesize = SG_ALL, + .use_clustering = ENABLE_CLUSTERING, + .cmd_per_lun = 1, + .can_queue = 1, +}; + +MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>"); +MODULE_DESCRIPTION("SCSI over IEEE1394"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(ieee1394, sbp2_id_table); + +/* Provide a module alias so root-on-sbp2 initrds don't break. */ +#ifndef CONFIG_IEEE1394_SBP2_MODULE +MODULE_ALIAS("sbp2"); +#endif + +static int __init sbp2_init(void) +{ + return driver_register(&sbp2_driver.driver); +} + +static void __exit sbp2_cleanup(void) +{ + driver_unregister(&sbp2_driver.driver); +} + +module_init(sbp2_init); +module_exit(sbp2_cleanup); diff --git a/drivers/firewire/fw-topology.c b/drivers/firewire/fw-topology.c new file mode 100644 index 000000000000..7aebb8ae0efa --- /dev/null +++ b/drivers/firewire/fw-topology.c @@ -0,0 +1,537 @@ +/* + * Incremental bus scan, based on bus topology + * + * Copyright (C) 2004-2006 Kristian Hoegsberg <krh@bitplanet.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/wait.h> +#include <linux/errno.h> +#include "fw-transaction.h" +#include "fw-topology.h" + +#define SELF_ID_PHY_ID(q) (((q) >> 24) & 0x3f) +#define SELF_ID_EXTENDED(q) (((q) >> 23) & 0x01) +#define SELF_ID_LINK_ON(q) (((q) >> 22) & 0x01) +#define SELF_ID_GAP_COUNT(q) (((q) >> 16) & 0x3f) +#define SELF_ID_PHY_SPEED(q) (((q) >> 14) & 0x03) +#define SELF_ID_CONTENDER(q) (((q) >> 11) & 0x01) +#define SELF_ID_PHY_INITIATOR(q) (((q) >> 1) & 0x01) +#define SELF_ID_MORE_PACKETS(q) (((q) >> 0) & 0x01) + +#define SELF_ID_EXT_SEQUENCE(q) (((q) >> 20) & 0x07) + +static u32 *count_ports(u32 *sid, int *total_port_count, int *child_port_count) +{ + u32 q; + int port_type, shift, seq; + + *total_port_count = 0; + *child_port_count = 0; + + shift = 6; + q = *sid; + seq = 0; + + while (1) { + port_type = (q >> shift) & 0x03; + switch (port_type) { + case SELFID_PORT_CHILD: + (*child_port_count)++; + case SELFID_PORT_PARENT: + case SELFID_PORT_NCONN: + (*total_port_count)++; + case SELFID_PORT_NONE: + break; + } + + shift -= 2; + if (shift == 0) { + if (!SELF_ID_MORE_PACKETS(q)) + return sid + 1; + + shift = 16; + sid++; + q = *sid; + + /* + * Check that the extra packets actually are + * extended self ID packets and that the + * sequence numbers in the extended self ID + * packets increase as expected. + */ + + if (!SELF_ID_EXTENDED(q) || + seq != SELF_ID_EXT_SEQUENCE(q)) + return NULL; + + seq++; + } + } +} + +static int get_port_type(u32 *sid, int port_index) +{ + int index, shift; + + index = (port_index + 5) / 8; + shift = 16 - ((port_index + 5) & 7) * 2; + return (sid[index] >> shift) & 0x03; +} + +static struct fw_node *fw_node_create(u32 sid, int port_count, int color) +{ + struct fw_node *node; + + node = kzalloc(sizeof(*node) + port_count * sizeof(node->ports[0]), + GFP_ATOMIC); + if (node == NULL) + return NULL; + + node->color = color; + node->node_id = LOCAL_BUS | SELF_ID_PHY_ID(sid); + node->link_on = SELF_ID_LINK_ON(sid); + node->phy_speed = SELF_ID_PHY_SPEED(sid); + node->port_count = port_count; + + atomic_set(&node->ref_count, 1); + INIT_LIST_HEAD(&node->link); + + return node; +} + +/* + * Compute the maximum hop count for this node and it's children. The + * maximum hop count is the maximum number of connections between any + * two nodes in the subtree rooted at this node. We need this for + * setting the gap count. As we build the tree bottom up in + * build_tree() below, this is fairly easy to do: for each node we + * maintain the max hop count and the max depth, ie the number of hops + * to the furthest leaf. Computing the max hop count breaks down into + * two cases: either the path goes through this node, in which case + * the hop count is the sum of the two biggest child depths plus 2. + * Or it could be the case that the max hop path is entirely + * containted in a child tree, in which case the max hop count is just + * the max hop count of this child. + */ +static void update_hop_count(struct fw_node *node) +{ + int depths[2] = { -1, -1 }; + int max_child_hops = 0; + int i; + + for (i = 0; i < node->port_count; i++) { + if (node->ports[i].node == NULL) + continue; + + if (node->ports[i].node->max_hops > max_child_hops) + max_child_hops = node->ports[i].node->max_hops; + + if (node->ports[i].node->max_depth > depths[0]) { + depths[1] = depths[0]; + depths[0] = node->ports[i].node->max_depth; + } else if (node->ports[i].node->max_depth > depths[1]) + depths[1] = node->ports[i].node->max_depth; + } + + node->max_depth = depths[0] + 1; + node->max_hops = max(max_child_hops, depths[0] + depths[1] + 2); +} + + +/** + * build_tree - Build the tree representation of the topology + * @self_ids: array of self IDs to create the tree from + * @self_id_count: the length of the self_ids array + * @local_id: the node ID of the local node + * + * This function builds the tree representation of the topology given + * by the self IDs from the latest bus reset. During the construction + * of the tree, the function checks that the self IDs are valid and + * internally consistent. On succcess this funtions returns the + * fw_node corresponding to the local card otherwise NULL. + */ +static struct fw_node *build_tree(struct fw_card *card, + u32 *sid, int self_id_count) +{ + struct fw_node *node, *child, *local_node, *irm_node; + struct list_head stack, *h; + u32 *next_sid, *end, q; + int i, port_count, child_port_count, phy_id, parent_count, stack_depth; + int gap_count, topology_type; + + local_node = NULL; + node = NULL; + INIT_LIST_HEAD(&stack); + stack_depth = 0; + end = sid + self_id_count; + phy_id = 0; + irm_node = NULL; + gap_count = SELF_ID_GAP_COUNT(*sid); + topology_type = 0; + + while (sid < end) { + next_sid = count_ports(sid, &port_count, &child_port_count); + + if (next_sid == NULL) { + fw_error("Inconsistent extended self IDs.\n"); + return NULL; + } + + q = *sid; + if (phy_id != SELF_ID_PHY_ID(q)) { + fw_error("PHY ID mismatch in self ID: %d != %d.\n", + phy_id, SELF_ID_PHY_ID(q)); + return NULL; + } + + if (child_port_count > stack_depth) { + fw_error("Topology stack underflow\n"); + return NULL; + } + + /* + * Seek back from the top of our stack to find the + * start of the child nodes for this node. + */ + for (i = 0, h = &stack; i < child_port_count; i++) + h = h->prev; + child = fw_node(h); + + node = fw_node_create(q, port_count, card->color); + if (node == NULL) { + fw_error("Out of memory while building topology."); + return NULL; + } + + if (phy_id == (card->node_id & 0x3f)) + local_node = node; + + if (SELF_ID_CONTENDER(q)) + irm_node = node; + + if (node->phy_speed == SCODE_BETA) + topology_type |= FW_TOPOLOGY_B; + else + topology_type |= FW_TOPOLOGY_A; + + parent_count = 0; + + for (i = 0; i < port_count; i++) { + switch (get_port_type(sid, i)) { + case SELFID_PORT_PARENT: + /* + * Who's your daddy? We dont know the + * parent node at this time, so we + * temporarily abuse node->color for + * remembering the entry in the + * node->ports array where the parent + * node should be. Later, when we + * handle the parent node, we fix up + * the reference. + */ + parent_count++; + node->color = i; + break; + + case SELFID_PORT_CHILD: + node->ports[i].node = child; + /* + * Fix up parent reference for this + * child node. + */ + child->ports[child->color].node = node; + child->color = card->color; + child = fw_node(child->link.next); + break; + } + } + + /* + * Check that the node reports exactly one parent + * port, except for the root, which of course should + * have no parents. + */ + if ((next_sid == end && parent_count != 0) || + (next_sid < end && parent_count != 1)) { + fw_error("Parent port inconsistency for node %d: " + "parent_count=%d\n", phy_id, parent_count); + return NULL; + } + + /* Pop the child nodes off the stack and push the new node. */ + __list_del(h->prev, &stack); + list_add_tail(&node->link, &stack); + stack_depth += 1 - child_port_count; + + /* + * If all PHYs does not report the same gap count + * setting, we fall back to 63 which will force a gap + * count reconfiguration and a reset. + */ + if (SELF_ID_GAP_COUNT(q) != gap_count) + gap_count = 63; + + update_hop_count(node); + + sid = next_sid; + phy_id++; + } + + card->root_node = node; + card->irm_node = irm_node; + card->gap_count = gap_count; + card->topology_type = topology_type; + + return local_node; +} + +typedef void (*fw_node_callback_t)(struct fw_card * card, + struct fw_node * node, + struct fw_node * parent); + +static void +for_each_fw_node(struct fw_card *card, struct fw_node *root, + fw_node_callback_t callback) +{ + struct list_head list; + struct fw_node *node, *next, *child, *parent; + int i; + + INIT_LIST_HEAD(&list); + + fw_node_get(root); + list_add_tail(&root->link, &list); + parent = NULL; + list_for_each_entry(node, &list, link) { + node->color = card->color; + + for (i = 0; i < node->port_count; i++) { + child = node->ports[i].node; + if (!child) + continue; + if (child->color == card->color) + parent = child; + else { + fw_node_get(child); + list_add_tail(&child->link, &list); + } + } + + callback(card, node, parent); + } + + list_for_each_entry_safe(node, next, &list, link) + fw_node_put(node); +} + +static void +report_lost_node(struct fw_card *card, + struct fw_node *node, struct fw_node *parent) +{ + fw_node_event(card, node, FW_NODE_DESTROYED); + fw_node_put(node); +} + +static void +report_found_node(struct fw_card *card, + struct fw_node *node, struct fw_node *parent) +{ + int b_path = (node->phy_speed == SCODE_BETA); + + if (parent != NULL) { + /* min() macro doesn't work here with gcc 3.4 */ + node->max_speed = parent->max_speed < node->phy_speed ? + parent->max_speed : node->phy_speed; + node->b_path = parent->b_path && b_path; + } else { + node->max_speed = node->phy_speed; + node->b_path = b_path; + } + + fw_node_event(card, node, FW_NODE_CREATED); +} + +void fw_destroy_nodes(struct fw_card *card) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + card->color++; + if (card->local_node != NULL) + for_each_fw_node(card, card->local_node, report_lost_node); + spin_unlock_irqrestore(&card->lock, flags); +} + +static void move_tree(struct fw_node *node0, struct fw_node *node1, int port) +{ + struct fw_node *tree; + int i; + + tree = node1->ports[port].node; + node0->ports[port].node = tree; + for (i = 0; i < tree->port_count; i++) { + if (tree->ports[i].node == node1) { + tree->ports[i].node = node0; + break; + } + } +} + +/** + * update_tree - compare the old topology tree for card with the new + * one specified by root. Queue the nodes and mark them as either + * found, lost or updated. Update the nodes in the card topology tree + * as we go. + */ +static void +update_tree(struct fw_card *card, struct fw_node *root) +{ + struct list_head list0, list1; + struct fw_node *node0, *node1; + int i, event; + + INIT_LIST_HEAD(&list0); + list_add_tail(&card->local_node->link, &list0); + INIT_LIST_HEAD(&list1); + list_add_tail(&root->link, &list1); + + node0 = fw_node(list0.next); + node1 = fw_node(list1.next); + + while (&node0->link != &list0) { + + /* assert(node0->port_count == node1->port_count); */ + if (node0->link_on && !node1->link_on) + event = FW_NODE_LINK_OFF; + else if (!node0->link_on && node1->link_on) + event = FW_NODE_LINK_ON; + else + event = FW_NODE_UPDATED; + + node0->node_id = node1->node_id; + node0->color = card->color; + node0->link_on = node1->link_on; + node0->initiated_reset = node1->initiated_reset; + node0->max_hops = node1->max_hops; + node1->color = card->color; + fw_node_event(card, node0, event); + + if (card->root_node == node1) + card->root_node = node0; + if (card->irm_node == node1) + card->irm_node = node0; + + for (i = 0; i < node0->port_count; i++) { + if (node0->ports[i].node && node1->ports[i].node) { + /* + * This port didn't change, queue the + * connected node for further + * investigation. + */ + if (node0->ports[i].node->color == card->color) + continue; + list_add_tail(&node0->ports[i].node->link, + &list0); + list_add_tail(&node1->ports[i].node->link, + &list1); + } else if (node0->ports[i].node) { + /* + * The nodes connected here were + * unplugged; unref the lost nodes and + * queue FW_NODE_LOST callbacks for + * them. + */ + + for_each_fw_node(card, node0->ports[i].node, + report_lost_node); + node0->ports[i].node = NULL; + } else if (node1->ports[i].node) { + /* + * One or more node were connected to + * this port. Move the new nodes into + * the tree and queue FW_NODE_CREATED + * callbacks for them. + */ + move_tree(node0, node1, i); + for_each_fw_node(card, node0->ports[i].node, + report_found_node); + } + } + + node0 = fw_node(node0->link.next); + node1 = fw_node(node1->link.next); + } +} + +static void +update_topology_map(struct fw_card *card, u32 *self_ids, int self_id_count) +{ + int node_count; + + card->topology_map[1]++; + node_count = (card->root_node->node_id & 0x3f) + 1; + card->topology_map[2] = (node_count << 16) | self_id_count; + card->topology_map[0] = (self_id_count + 2) << 16; + memcpy(&card->topology_map[3], self_ids, self_id_count * 4); + fw_compute_block_crc(card->topology_map); +} + +void +fw_core_handle_bus_reset(struct fw_card *card, + int node_id, int generation, + int self_id_count, u32 * self_ids) +{ + struct fw_node *local_node; + unsigned long flags; + + fw_flush_transactions(card); + + spin_lock_irqsave(&card->lock, flags); + + /* + * If the new topology has a different self_id_count the topology + * changed, either nodes were added or removed. In that case we + * reset the IRM reset counter. + */ + if (card->self_id_count != self_id_count) + card->bm_retries = 0; + + card->node_id = node_id; + card->generation = generation; + card->reset_jiffies = jiffies; + schedule_delayed_work(&card->work, 0); + + local_node = build_tree(card, self_ids, self_id_count); + + update_topology_map(card, self_ids, self_id_count); + + card->color++; + + if (local_node == NULL) { + fw_error("topology build failed\n"); + /* FIXME: We need to issue a bus reset in this case. */ + } else if (card->local_node == NULL) { + card->local_node = local_node; + for_each_fw_node(card, local_node, report_found_node); + } else { + update_tree(card, local_node); + } + + spin_unlock_irqrestore(&card->lock, flags); +} +EXPORT_SYMBOL(fw_core_handle_bus_reset); diff --git a/drivers/firewire/fw-topology.h b/drivers/firewire/fw-topology.h new file mode 100644 index 000000000000..363b6cbcd0b3 --- /dev/null +++ b/drivers/firewire/fw-topology.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2003-2006 Kristian Hoegsberg <krh@bitplanet.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __fw_topology_h +#define __fw_topology_h + +enum { + FW_TOPOLOGY_A = 0x01, + FW_TOPOLOGY_B = 0x02, + FW_TOPOLOGY_MIXED = 0x03, +}; + +enum { + FW_NODE_CREATED = 0x00, + FW_NODE_UPDATED = 0x01, + FW_NODE_DESTROYED = 0x02, + FW_NODE_LINK_ON = 0x03, + FW_NODE_LINK_OFF = 0x04, +}; + +struct fw_port { + struct fw_node *node; + unsigned speed : 3; /* S100, S200, ... S3200 */ +}; + +struct fw_node { + u16 node_id; + u8 color; + u8 port_count; + unsigned link_on : 1; + unsigned initiated_reset : 1; + unsigned b_path : 1; + u8 phy_speed : 3; /* As in the self ID packet. */ + u8 max_speed : 5; /* Minimum of all phy-speeds and port speeds on + * the path from the local node to this node. */ + u8 max_depth : 4; /* Maximum depth to any leaf node */ + u8 max_hops : 4; /* Max hops in this sub tree */ + atomic_t ref_count; + + /* For serializing node topology into a list. */ + struct list_head link; + + /* Upper layer specific data. */ + void *data; + + struct fw_port ports[0]; +}; + +static inline struct fw_node * +fw_node(struct list_head *l) +{ + return list_entry(l, struct fw_node, link); +} + +static inline struct fw_node * +fw_node_get(struct fw_node *node) +{ + atomic_inc(&node->ref_count); + + return node; +} + +static inline void +fw_node_put(struct fw_node *node) +{ + if (atomic_dec_and_test(&node->ref_count)) + kfree(node); +} + +void +fw_destroy_nodes(struct fw_card *card); + +int +fw_compute_block_crc(u32 *block); + + +#endif /* __fw_topology_h */ diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c new file mode 100644 index 000000000000..80d0121463d0 --- /dev/null +++ b/drivers/firewire/fw-transaction.c @@ -0,0 +1,910 @@ +/* + * Core IEEE1394 transaction logic + * + * Copyright (C) 2004-2006 Kristian Hoegsberg <krh@bitplanet.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/poll.h> +#include <linux/list.h> +#include <linux/kthread.h> +#include <asm/uaccess.h> +#include <asm/semaphore.h> + +#include "fw-transaction.h" +#include "fw-topology.h" +#include "fw-device.h" + +#define HEADER_PRI(pri) ((pri) << 0) +#define HEADER_TCODE(tcode) ((tcode) << 4) +#define HEADER_RETRY(retry) ((retry) << 8) +#define HEADER_TLABEL(tlabel) ((tlabel) << 10) +#define HEADER_DESTINATION(destination) ((destination) << 16) +#define HEADER_SOURCE(source) ((source) << 16) +#define HEADER_RCODE(rcode) ((rcode) << 12) +#define HEADER_OFFSET_HIGH(offset_high) ((offset_high) << 0) +#define HEADER_DATA_LENGTH(length) ((length) << 16) +#define HEADER_EXTENDED_TCODE(tcode) ((tcode) << 0) + +#define HEADER_GET_TCODE(q) (((q) >> 4) & 0x0f) +#define HEADER_GET_TLABEL(q) (((q) >> 10) & 0x3f) +#define HEADER_GET_RCODE(q) (((q) >> 12) & 0x0f) +#define HEADER_GET_DESTINATION(q) (((q) >> 16) & 0xffff) +#define HEADER_GET_SOURCE(q) (((q) >> 16) & 0xffff) +#define HEADER_GET_OFFSET_HIGH(q) (((q) >> 0) & 0xffff) +#define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff) +#define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff) + +#define PHY_CONFIG_GAP_COUNT(gap_count) (((gap_count) << 16) | (1 << 22)) +#define PHY_CONFIG_ROOT_ID(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23)) +#define PHY_IDENTIFIER(id) ((id) << 30) + +static int +close_transaction(struct fw_transaction *transaction, + struct fw_card *card, int rcode, + u32 *payload, size_t length) +{ + struct fw_transaction *t; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + list_for_each_entry(t, &card->transaction_list, link) { + if (t == transaction) { + list_del(&t->link); + card->tlabel_mask &= ~(1 << t->tlabel); + break; + } + } + spin_unlock_irqrestore(&card->lock, flags); + + if (&t->link != &card->transaction_list) { + t->callback(card, rcode, payload, length, t->callback_data); + return 0; + } + + return -ENOENT; +} + +/* + * Only valid for transactions that are potentially pending (ie have + * been sent). + */ +int +fw_cancel_transaction(struct fw_card *card, + struct fw_transaction *transaction) +{ + /* + * Cancel the packet transmission if it's still queued. That + * will call the packet transmission callback which cancels + * the transaction. + */ + + if (card->driver->cancel_packet(card, &transaction->packet) == 0) + return 0; + + /* + * If the request packet has already been sent, we need to see + * if the transaction is still pending and remove it in that case. + */ + + return close_transaction(transaction, card, RCODE_CANCELLED, NULL, 0); +} +EXPORT_SYMBOL(fw_cancel_transaction); + +static void +transmit_complete_callback(struct fw_packet *packet, + struct fw_card *card, int status) +{ + struct fw_transaction *t = + container_of(packet, struct fw_transaction, packet); + + switch (status) { + case ACK_COMPLETE: + close_transaction(t, card, RCODE_COMPLETE, NULL, 0); + break; + case ACK_PENDING: + t->timestamp = packet->timestamp; + break; + case ACK_BUSY_X: + case ACK_BUSY_A: + case ACK_BUSY_B: + close_transaction(t, card, RCODE_BUSY, NULL, 0); + break; + case ACK_DATA_ERROR: + close_transaction(t, card, RCODE_DATA_ERROR, NULL, 0); + break; + case ACK_TYPE_ERROR: + close_transaction(t, card, RCODE_TYPE_ERROR, NULL, 0); + break; + default: + /* + * In this case the ack is really a juju specific + * rcode, so just forward that to the callback. + */ + close_transaction(t, card, status, NULL, 0); + break; + } +} + +static void +fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, + int node_id, int source_id, int generation, int speed, + unsigned long long offset, void *payload, size_t length) +{ + int ext_tcode; + + if (tcode > 0x10) { + ext_tcode = tcode - 0x10; + tcode = TCODE_LOCK_REQUEST; + } else + ext_tcode = 0; + + packet->header[0] = + HEADER_RETRY(RETRY_X) | + HEADER_TLABEL(tlabel) | + HEADER_TCODE(tcode) | + HEADER_DESTINATION(node_id); + packet->header[1] = + HEADER_OFFSET_HIGH(offset >> 32) | HEADER_SOURCE(source_id); + packet->header[2] = + offset; + + switch (tcode) { + case TCODE_WRITE_QUADLET_REQUEST: + packet->header[3] = *(u32 *)payload; + packet->header_length = 16; + packet->payload_length = 0; + break; + + case TCODE_LOCK_REQUEST: + case TCODE_WRITE_BLOCK_REQUEST: + packet->header[3] = + HEADER_DATA_LENGTH(length) | + HEADER_EXTENDED_TCODE(ext_tcode); + packet->header_length = 16; + packet->payload = payload; + packet->payload_length = length; + break; + + case TCODE_READ_QUADLET_REQUEST: + packet->header_length = 12; + packet->payload_length = 0; + break; + + case TCODE_READ_BLOCK_REQUEST: + packet->header[3] = + HEADER_DATA_LENGTH(length) | + HEADER_EXTENDED_TCODE(ext_tcode); + packet->header_length = 16; + packet->payload_length = 0; + break; + } + + packet->speed = speed; + packet->generation = generation; + packet->ack = 0; +} + +/** + * This function provides low-level access to the IEEE1394 transaction + * logic. Most C programs would use either fw_read(), fw_write() or + * fw_lock() instead - those function are convenience wrappers for + * this function. The fw_send_request() function is primarily + * provided as a flexible, one-stop entry point for languages bindings + * and protocol bindings. + * + * FIXME: Document this function further, in particular the possible + * values for rcode in the callback. In short, we map ACK_COMPLETE to + * RCODE_COMPLETE, internal errors set errno and set rcode to + * RCODE_SEND_ERROR (which is out of range for standard ieee1394 + * rcodes). All other rcodes are forwarded unchanged. For all + * errors, payload is NULL, length is 0. + * + * Can not expect the callback to be called before the function + * returns, though this does happen in some cases (ACK_COMPLETE and + * errors). + * + * The payload is only used for write requests and must not be freed + * until the callback has been called. + * + * @param card the card from which to send the request + * @param tcode the tcode for this transaction. Do not use + * TCODE_LOCK_REQUEST directly, insted use TCODE_LOCK_MASK_SWAP + * etc. to specify tcode and ext_tcode. + * @param node_id the destination node ID (bus ID and PHY ID concatenated) + * @param generation the generation for which node_id is valid + * @param speed the speed to use for sending the request + * @param offset the 48 bit offset on the destination node + * @param payload the data payload for the request subaction + * @param length the length in bytes of the data to read + * @param callback function to be called when the transaction is completed + * @param callback_data pointer to arbitrary data, which will be + * passed to the callback + */ +void +fw_send_request(struct fw_card *card, struct fw_transaction *t, + int tcode, int node_id, int generation, int speed, + unsigned long long offset, + void *payload, size_t length, + fw_transaction_callback_t callback, void *callback_data) +{ + unsigned long flags; + int tlabel, source; + + /* + * Bump the flush timer up 100ms first of all so we + * don't race with a flush timer callback. + */ + + mod_timer(&card->flush_timer, jiffies + DIV_ROUND_UP(HZ, 10)); + + /* + * Allocate tlabel from the bitmap and put the transaction on + * the list while holding the card spinlock. + */ + + spin_lock_irqsave(&card->lock, flags); + + source = card->node_id; + tlabel = card->current_tlabel; + if (card->tlabel_mask & (1 << tlabel)) { + spin_unlock_irqrestore(&card->lock, flags); + callback(card, RCODE_SEND_ERROR, NULL, 0, callback_data); + return; + } + + card->current_tlabel = (card->current_tlabel + 1) & 0x1f; + card->tlabel_mask |= (1 << tlabel); + + list_add_tail(&t->link, &card->transaction_list); + + spin_unlock_irqrestore(&card->lock, flags); + + /* Initialize rest of transaction, fill out packet and send it. */ + t->node_id = node_id; + t->tlabel = tlabel; + t->callback = callback; + t->callback_data = callback_data; + + fw_fill_request(&t->packet, tcode, t->tlabel, + node_id, source, generation, + speed, offset, payload, length); + t->packet.callback = transmit_complete_callback; + + card->driver->send_request(card, &t->packet); +} +EXPORT_SYMBOL(fw_send_request); + +static void +transmit_phy_packet_callback(struct fw_packet *packet, + struct fw_card *card, int status) +{ + kfree(packet); +} + +static void send_phy_packet(struct fw_card *card, u32 data, int generation) +{ + struct fw_packet *packet; + + packet = kzalloc(sizeof(*packet), GFP_ATOMIC); + if (packet == NULL) + return; + + packet->header[0] = data; + packet->header[1] = ~data; + packet->header_length = 8; + packet->payload_length = 0; + packet->speed = SCODE_100; + packet->generation = generation; + packet->callback = transmit_phy_packet_callback; + + card->driver->send_request(card, packet); +} + +void fw_send_phy_config(struct fw_card *card, + int node_id, int generation, int gap_count) +{ + u32 q; + + q = PHY_IDENTIFIER(PHY_PACKET_CONFIG) | + PHY_CONFIG_ROOT_ID(node_id) | + PHY_CONFIG_GAP_COUNT(gap_count); + + send_phy_packet(card, q, generation); +} + +void fw_flush_transactions(struct fw_card *card) +{ + struct fw_transaction *t, *next; + struct list_head list; + unsigned long flags; + + INIT_LIST_HEAD(&list); + spin_lock_irqsave(&card->lock, flags); + list_splice_init(&card->transaction_list, &list); + card->tlabel_mask = 0; + spin_unlock_irqrestore(&card->lock, flags); + + list_for_each_entry_safe(t, next, &list, link) { + card->driver->cancel_packet(card, &t->packet); + + /* + * At this point cancel_packet will never call the + * transaction callback, since we just took all the + * transactions out of the list. So do it here. + */ + t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data); + } +} + +static struct fw_address_handler * +lookup_overlapping_address_handler(struct list_head *list, + unsigned long long offset, size_t length) +{ + struct fw_address_handler *handler; + + list_for_each_entry(handler, list, link) { + if (handler->offset < offset + length && + offset < handler->offset + handler->length) + return handler; + } + + return NULL; +} + +static struct fw_address_handler * +lookup_enclosing_address_handler(struct list_head *list, + unsigned long long offset, size_t length) +{ + struct fw_address_handler *handler; + + list_for_each_entry(handler, list, link) { + if (handler->offset <= offset && + offset + length <= handler->offset + handler->length) + return handler; + } + + return NULL; +} + +static DEFINE_SPINLOCK(address_handler_lock); +static LIST_HEAD(address_handler_list); + +const struct fw_address_region fw_low_memory_region = + { .start = 0x000000000000ULL, .end = 0x000100000000ULL, }; +const struct fw_address_region fw_high_memory_region = + { .start = 0x000100000000ULL, .end = 0xffffe0000000ULL, }; +const struct fw_address_region fw_private_region = + { .start = 0xffffe0000000ULL, .end = 0xfffff0000000ULL, }; +const struct fw_address_region fw_csr_region = + { .start = 0xfffff0000000ULL, .end = 0xfffff0000800ULL, }; +const struct fw_address_region fw_unit_space_region = + { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, }; +EXPORT_SYMBOL(fw_low_memory_region); +EXPORT_SYMBOL(fw_high_memory_region); +EXPORT_SYMBOL(fw_private_region); +EXPORT_SYMBOL(fw_csr_region); +EXPORT_SYMBOL(fw_unit_space_region); + +/** + * Allocate a range of addresses in the node space of the OHCI + * controller. When a request is received that falls within the + * specified address range, the specified callback is invoked. The + * parameters passed to the callback give the details of the + * particular request + */ +int +fw_core_add_address_handler(struct fw_address_handler *handler, + const struct fw_address_region *region) +{ + struct fw_address_handler *other; + unsigned long flags; + int ret = -EBUSY; + + spin_lock_irqsave(&address_handler_lock, flags); + + handler->offset = region->start; + while (handler->offset + handler->length <= region->end) { + other = + lookup_overlapping_address_handler(&address_handler_list, + handler->offset, + handler->length); + if (other != NULL) { + handler->offset += other->length; + } else { + list_add_tail(&handler->link, &address_handler_list); + ret = 0; + break; + } + } + + spin_unlock_irqrestore(&address_handler_lock, flags); + + return ret; +} +EXPORT_SYMBOL(fw_core_add_address_handler); + +/** + * Deallocate a range of addresses allocated with fw_allocate. This + * will call the associated callback one last time with a the special + * tcode TCODE_DEALLOCATE, to let the client destroy the registered + * callback data. For convenience, the callback parameters offset and + * length are set to the start and the length respectively for the + * deallocated region, payload is set to NULL. + */ +void fw_core_remove_address_handler(struct fw_address_handler *handler) +{ + unsigned long flags; + + spin_lock_irqsave(&address_handler_lock, flags); + list_del(&handler->link); + spin_unlock_irqrestore(&address_handler_lock, flags); +} +EXPORT_SYMBOL(fw_core_remove_address_handler); + +struct fw_request { + struct fw_packet response; + u32 request_header[4]; + int ack; + u32 length; + u32 data[0]; +}; + +static void +free_response_callback(struct fw_packet *packet, + struct fw_card *card, int status) +{ + struct fw_request *request; + + request = container_of(packet, struct fw_request, response); + kfree(request); +} + +void +fw_fill_response(struct fw_packet *response, u32 *request_header, + int rcode, void *payload, size_t length) +{ + int tcode, tlabel, extended_tcode, source, destination; + + tcode = HEADER_GET_TCODE(request_header[0]); + tlabel = HEADER_GET_TLABEL(request_header[0]); + source = HEADER_GET_DESTINATION(request_header[0]); + destination = HEADER_GET_SOURCE(request_header[1]); + extended_tcode = HEADER_GET_EXTENDED_TCODE(request_header[3]); + + response->header[0] = + HEADER_RETRY(RETRY_1) | + HEADER_TLABEL(tlabel) | + HEADER_DESTINATION(destination); + response->header[1] = + HEADER_SOURCE(source) | + HEADER_RCODE(rcode); + response->header[2] = 0; + + switch (tcode) { + case TCODE_WRITE_QUADLET_REQUEST: + case TCODE_WRITE_BLOCK_REQUEST: + response->header[0] |= HEADER_TCODE(TCODE_WRITE_RESPONSE); + response->header_length = 12; + response->payload_length = 0; + break; + + case TCODE_READ_QUADLET_REQUEST: + response->header[0] |= + HEADER_TCODE(TCODE_READ_QUADLET_RESPONSE); + if (payload != NULL) + response->header[3] = *(u32 *)payload; + else + response->header[3] = 0; + response->header_length = 16; + response->payload_length = 0; + break; + + case TCODE_READ_BLOCK_REQUEST: + case TCODE_LOCK_REQUEST: + response->header[0] |= HEADER_TCODE(tcode + 2); + response->header[3] = + HEADER_DATA_LENGTH(length) | + HEADER_EXTENDED_TCODE(extended_tcode); + response->header_length = 16; + response->payload = payload; + response->payload_length = length; + break; + + default: + BUG(); + return; + } +} +EXPORT_SYMBOL(fw_fill_response); + +static struct fw_request * +allocate_request(struct fw_packet *p) +{ + struct fw_request *request; + u32 *data, length; + int request_tcode, t; + + request_tcode = HEADER_GET_TCODE(p->header[0]); + switch (request_tcode) { + case TCODE_WRITE_QUADLET_REQUEST: + data = &p->header[3]; + length = 4; + break; + + case TCODE_WRITE_BLOCK_REQUEST: + case TCODE_LOCK_REQUEST: + data = p->payload; + length = HEADER_GET_DATA_LENGTH(p->header[3]); + break; + + case TCODE_READ_QUADLET_REQUEST: + data = NULL; + length = 4; + break; + + case TCODE_READ_BLOCK_REQUEST: + data = NULL; + length = HEADER_GET_DATA_LENGTH(p->header[3]); + break; + + default: + BUG(); + return NULL; + } + + request = kmalloc(sizeof(*request) + length, GFP_ATOMIC); + if (request == NULL) + return NULL; + + t = (p->timestamp & 0x1fff) + 4000; + if (t >= 8000) + t = (p->timestamp & ~0x1fff) + 0x2000 + t - 8000; + else + t = (p->timestamp & ~0x1fff) + t; + + request->response.speed = p->speed; + request->response.timestamp = t; + request->response.generation = p->generation; + request->response.ack = 0; + request->response.callback = free_response_callback; + request->ack = p->ack; + request->length = length; + if (data) + memcpy(request->data, data, length); + + memcpy(request->request_header, p->header, sizeof(p->header)); + + return request; +} + +void +fw_send_response(struct fw_card *card, struct fw_request *request, int rcode) +{ + /* + * Broadcast packets are reported as ACK_COMPLETE, so this + * check is sufficient to ensure we don't send response to + * broadcast packets or posted writes. + */ + if (request->ack != ACK_PENDING) + return; + + if (rcode == RCODE_COMPLETE) + fw_fill_response(&request->response, request->request_header, + rcode, request->data, request->length); + else + fw_fill_response(&request->response, request->request_header, + rcode, NULL, 0); + + card->driver->send_response(card, &request->response); +} +EXPORT_SYMBOL(fw_send_response); + +void +fw_core_handle_request(struct fw_card *card, struct fw_packet *p) +{ + struct fw_address_handler *handler; + struct fw_request *request; + unsigned long long offset; + unsigned long flags; + int tcode, destination, source; + + if (p->payload_length > 2048) { + /* FIXME: send error response. */ + return; + } + + if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE) + return; + + request = allocate_request(p); + if (request == NULL) { + /* FIXME: send statically allocated busy packet. */ + return; + } + + offset = + ((unsigned long long) + HEADER_GET_OFFSET_HIGH(p->header[1]) << 32) | p->header[2]; + tcode = HEADER_GET_TCODE(p->header[0]); + destination = HEADER_GET_DESTINATION(p->header[0]); + source = HEADER_GET_SOURCE(p->header[0]); + + spin_lock_irqsave(&address_handler_lock, flags); + handler = lookup_enclosing_address_handler(&address_handler_list, + offset, request->length); + spin_unlock_irqrestore(&address_handler_lock, flags); + + /* + * FIXME: lookup the fw_node corresponding to the sender of + * this request and pass that to the address handler instead + * of the node ID. We may also want to move the address + * allocations to fw_node so we only do this callback if the + * upper layers registered it for this node. + */ + + if (handler == NULL) + fw_send_response(card, request, RCODE_ADDRESS_ERROR); + else + handler->address_callback(card, request, + tcode, destination, source, + p->generation, p->speed, offset, + request->data, request->length, + handler->callback_data); +} +EXPORT_SYMBOL(fw_core_handle_request); + +void +fw_core_handle_response(struct fw_card *card, struct fw_packet *p) +{ + struct fw_transaction *t; + unsigned long flags; + u32 *data; + size_t data_length; + int tcode, tlabel, destination, source, rcode; + + tcode = HEADER_GET_TCODE(p->header[0]); + tlabel = HEADER_GET_TLABEL(p->header[0]); + destination = HEADER_GET_DESTINATION(p->header[0]); + source = HEADER_GET_SOURCE(p->header[1]); + rcode = HEADER_GET_RCODE(p->header[1]); + + spin_lock_irqsave(&card->lock, flags); + list_for_each_entry(t, &card->transaction_list, link) { + if (t->node_id == source && t->tlabel == tlabel) { + list_del(&t->link); + card->tlabel_mask &= ~(1 << t->tlabel); + break; + } + } + spin_unlock_irqrestore(&card->lock, flags); + + if (&t->link == &card->transaction_list) { + fw_notify("Unsolicited response (source %x, tlabel %x)\n", + source, tlabel); + return; + } + + /* + * FIXME: sanity check packet, is length correct, does tcodes + * and addresses match. + */ + + switch (tcode) { + case TCODE_READ_QUADLET_RESPONSE: + data = (u32 *) &p->header[3]; + data_length = 4; + break; + + case TCODE_WRITE_RESPONSE: + data = NULL; + data_length = 0; + break; + + case TCODE_READ_BLOCK_RESPONSE: + case TCODE_LOCK_RESPONSE: + data = p->payload; + data_length = HEADER_GET_DATA_LENGTH(p->header[3]); + break; + + default: + /* Should never happen, this is just to shut up gcc. */ + data = NULL; + data_length = 0; + break; + } + + t->callback(card, rcode, data, data_length, t->callback_data); +} +EXPORT_SYMBOL(fw_core_handle_response); + +const struct fw_address_region topology_map_region = + { .start = 0xfffff0001000ull, .end = 0xfffff0001400ull, }; + +static void +handle_topology_map(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, int speed, + unsigned long long offset, + void *payload, size_t length, void *callback_data) +{ + int i, start, end; + u32 *map; + + if (!TCODE_IS_READ_REQUEST(tcode)) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + + if ((offset & 3) > 0 || (length & 3) > 0) { + fw_send_response(card, request, RCODE_ADDRESS_ERROR); + return; + } + + start = (offset - topology_map_region.start) / 4; + end = start + length / 4; + map = payload; + + for (i = 0; i < length / 4; i++) + map[i] = cpu_to_be32(card->topology_map[start + i]); + + fw_send_response(card, request, RCODE_COMPLETE); +} + +static struct fw_address_handler topology_map = { + .length = 0x200, + .address_callback = handle_topology_map, +}; + +const struct fw_address_region registers_region = + { .start = 0xfffff0000000ull, .end = 0xfffff0000400ull, }; + +static void +handle_registers(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, int speed, + unsigned long long offset, + void *payload, size_t length, void *callback_data) +{ + int reg = offset - CSR_REGISTER_BASE; + unsigned long long bus_time; + __be32 *data = payload; + + switch (reg) { + case CSR_CYCLE_TIME: + case CSR_BUS_TIME: + if (!TCODE_IS_READ_REQUEST(tcode) || length != 4) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + break; + } + + bus_time = card->driver->get_bus_time(card); + if (reg == CSR_CYCLE_TIME) + *data = cpu_to_be32(bus_time); + else + *data = cpu_to_be32(bus_time >> 25); + fw_send_response(card, request, RCODE_COMPLETE); + break; + + case CSR_BUS_MANAGER_ID: + case CSR_BANDWIDTH_AVAILABLE: + case CSR_CHANNELS_AVAILABLE_HI: + case CSR_CHANNELS_AVAILABLE_LO: + /* + * FIXME: these are handled by the OHCI hardware and + * the stack never sees these request. If we add + * support for a new type of controller that doesn't + * handle this in hardware we need to deal with these + * transactions. + */ + BUG(); + break; + + case CSR_BUSY_TIMEOUT: + /* FIXME: Implement this. */ + default: + fw_send_response(card, request, RCODE_ADDRESS_ERROR); + break; + } +} + +static struct fw_address_handler registers = { + .length = 0x400, + .address_callback = handle_registers, +}; + +MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>"); +MODULE_DESCRIPTION("Core IEEE1394 transaction logic"); +MODULE_LICENSE("GPL"); + +static const u32 vendor_textual_descriptor[] = { + /* textual descriptor leaf () */ + 0x00060000, + 0x00000000, + 0x00000000, + 0x4c696e75, /* L i n u */ + 0x78204669, /* x F i */ + 0x72657769, /* r e w i */ + 0x72650000, /* r e */ +}; + +static const u32 model_textual_descriptor[] = { + /* model descriptor leaf () */ + 0x00030000, + 0x00000000, + 0x00000000, + 0x4a756a75, /* J u j u */ +}; + +static struct fw_descriptor vendor_id_descriptor = { + .length = ARRAY_SIZE(vendor_textual_descriptor), + .immediate = 0x03d00d1e, + .key = 0x81000000, + .data = vendor_textual_descriptor, +}; + +static struct fw_descriptor model_id_descriptor = { + .length = ARRAY_SIZE(model_textual_descriptor), + .immediate = 0x17000001, + .key = 0x81000000, + .data = model_textual_descriptor, +}; + +static int __init fw_core_init(void) +{ + int retval; + + retval = bus_register(&fw_bus_type); + if (retval < 0) + return retval; + + fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops); + if (fw_cdev_major < 0) { + bus_unregister(&fw_bus_type); + return fw_cdev_major; + } + + retval = fw_core_add_address_handler(&topology_map, + &topology_map_region); + BUG_ON(retval < 0); + + retval = fw_core_add_address_handler(®isters, + ®isters_region); + BUG_ON(retval < 0); + + /* Add the vendor textual descriptor. */ + retval = fw_core_add_descriptor(&vendor_id_descriptor); + BUG_ON(retval < 0); + retval = fw_core_add_descriptor(&model_id_descriptor); + BUG_ON(retval < 0); + + return 0; +} + +static void __exit fw_core_cleanup(void) +{ + unregister_chrdev(fw_cdev_major, "firewire"); + bus_unregister(&fw_bus_type); +} + +module_init(fw_core_init); +module_exit(fw_core_cleanup); diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h new file mode 100644 index 000000000000..acdc3be38c61 --- /dev/null +++ b/drivers/firewire/fw-transaction.h @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2003-2006 Kristian Hoegsberg <krh@bitplanet.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __fw_transaction_h +#define __fw_transaction_h + +#include <linux/device.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/fs.h> +#include <linux/dma-mapping.h> +#include <linux/firewire-constants.h> + +#define TCODE_IS_READ_REQUEST(tcode) (((tcode) & ~1) == 4) +#define TCODE_IS_BLOCK_PACKET(tcode) (((tcode) & 1) != 0) +#define TCODE_IS_REQUEST(tcode) (((tcode) & 2) == 0) +#define TCODE_IS_RESPONSE(tcode) (((tcode) & 2) != 0) +#define TCODE_HAS_REQUEST_DATA(tcode) (((tcode) & 12) != 4) +#define TCODE_HAS_RESPONSE_DATA(tcode) (((tcode) & 12) != 0) + +#define LOCAL_BUS 0xffc0 + +#define SELFID_PORT_CHILD 0x3 +#define SELFID_PORT_PARENT 0x2 +#define SELFID_PORT_NCONN 0x1 +#define SELFID_PORT_NONE 0x0 + +#define PHY_PACKET_CONFIG 0x0 +#define PHY_PACKET_LINK_ON 0x1 +#define PHY_PACKET_SELF_ID 0x2 + +/* Bit fields _within_ the PHY registers. */ +#define PHY_LINK_ACTIVE 0x80 +#define PHY_CONTENDER 0x40 +#define PHY_BUS_RESET 0x40 +#define PHY_BUS_SHORT_RESET 0x40 + +#define CSR_REGISTER_BASE 0xfffff0000000ULL + +/* register offsets relative to CSR_REGISTER_BASE */ +#define CSR_STATE_CLEAR 0x0 +#define CSR_STATE_SET 0x4 +#define CSR_NODE_IDS 0x8 +#define CSR_RESET_START 0xc +#define CSR_SPLIT_TIMEOUT_HI 0x18 +#define CSR_SPLIT_TIMEOUT_LO 0x1c +#define CSR_CYCLE_TIME 0x200 +#define CSR_BUS_TIME 0x204 +#define CSR_BUSY_TIMEOUT 0x210 +#define CSR_BUS_MANAGER_ID 0x21c +#define CSR_BANDWIDTH_AVAILABLE 0x220 +#define CSR_CHANNELS_AVAILABLE 0x224 +#define CSR_CHANNELS_AVAILABLE_HI 0x224 +#define CSR_CHANNELS_AVAILABLE_LO 0x228 +#define CSR_BROADCAST_CHANNEL 0x234 +#define CSR_CONFIG_ROM 0x400 +#define CSR_CONFIG_ROM_END 0x800 +#define CSR_FCP_COMMAND 0xB00 +#define CSR_FCP_RESPONSE 0xD00 +#define CSR_FCP_END 0xF00 +#define CSR_TOPOLOGY_MAP 0x1000 +#define CSR_TOPOLOGY_MAP_END 0x1400 +#define CSR_SPEED_MAP 0x2000 +#define CSR_SPEED_MAP_END 0x3000 + +#define fw_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, ## args) +#define fw_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args) +#define fw_debug(s, args...) printk(KERN_DEBUG KBUILD_MODNAME ": " s, ## args) + +static inline void +fw_memcpy_from_be32(void *_dst, void *_src, size_t size) +{ + u32 *dst = _dst; + u32 *src = _src; + int i; + + for (i = 0; i < size / 4; i++) + dst[i] = cpu_to_be32(src[i]); +} + +static inline void +fw_memcpy_to_be32(void *_dst, void *_src, size_t size) +{ + fw_memcpy_from_be32(_dst, _src, size); +} + +struct fw_card; +struct fw_packet; +struct fw_node; +struct fw_request; + +struct fw_descriptor { + struct list_head link; + size_t length; + u32 immediate; + u32 key; + const u32 *data; +}; + +int fw_core_add_descriptor(struct fw_descriptor *desc); +void fw_core_remove_descriptor(struct fw_descriptor *desc); + +typedef void (*fw_packet_callback_t)(struct fw_packet *packet, + struct fw_card *card, int status); + +typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode, + void *data, + size_t length, + void *callback_data); + +typedef void (*fw_address_callback_t)(struct fw_card *card, + struct fw_request *request, + int tcode, int destination, int source, + int generation, int speed, + unsigned long long offset, + void *data, size_t length, + void *callback_data); + +typedef void (*fw_bus_reset_callback_t)(struct fw_card *handle, + int node_id, int generation, + u32 *self_ids, + int self_id_count, + void *callback_data); + +struct fw_packet { + int speed; + int generation; + u32 header[4]; + size_t header_length; + void *payload; + size_t payload_length; + u32 timestamp; + + /* + * This callback is called when the packet transmission has + * completed; for successful transmission, the status code is + * the ack received from the destination, otherwise it's a + * negative errno: ENOMEM, ESTALE, ETIMEDOUT, ENODEV, EIO. + * The callback can be called from tasklet context and thus + * must never block. + */ + fw_packet_callback_t callback; + int ack; + struct list_head link; + void *driver_data; +}; + +struct fw_transaction { + int node_id; /* The generation is implied; it is always the current. */ + int tlabel; + int timestamp; + struct list_head link; + + struct fw_packet packet; + + /* + * The data passed to the callback is valid only during the + * callback. + */ + fw_transaction_callback_t callback; + void *callback_data; +}; + +static inline struct fw_packet * +fw_packet(struct list_head *l) +{ + return list_entry(l, struct fw_packet, link); +} + +struct fw_address_handler { + u64 offset; + size_t length; + fw_address_callback_t address_callback; + void *callback_data; + struct list_head link; +}; + + +struct fw_address_region { + u64 start; + u64 end; +}; + +extern const struct fw_address_region fw_low_memory_region; +extern const struct fw_address_region fw_high_memory_region; +extern const struct fw_address_region fw_private_region; +extern const struct fw_address_region fw_csr_region; +extern const struct fw_address_region fw_unit_space_region; + +int fw_core_add_address_handler(struct fw_address_handler *handler, + const struct fw_address_region *region); +void fw_core_remove_address_handler(struct fw_address_handler *handler); +void fw_fill_response(struct fw_packet *response, u32 *request_header, + int rcode, void *payload, size_t length); +void fw_send_response(struct fw_card *card, + struct fw_request *request, int rcode); + +extern struct bus_type fw_bus_type; + +struct fw_card { + const struct fw_card_driver *driver; + struct device *device; + struct kref kref; + + int node_id; + int generation; + /* This is the generation used for timestamping incoming requests. */ + int request_generation; + int current_tlabel, tlabel_mask; + struct list_head transaction_list; + struct timer_list flush_timer; + unsigned long reset_jiffies; + + unsigned long long guid; + int max_receive; + int link_speed; + int config_rom_generation; + + /* + * We need to store up to 4 self ID for a maximum of 63 + * devices plus 3 words for the topology map header. + */ + int self_id_count; + u32 topology_map[252 + 3]; + + spinlock_t lock; /* Take this lock when handling the lists in + * this struct. */ + struct fw_node *local_node; + struct fw_node *root_node; + struct fw_node *irm_node; + int color; + int gap_count; + int topology_type; + + int index; + + struct list_head link; + + /* Work struct for BM duties. */ + struct delayed_work work; + int bm_retries; + int bm_generation; +}; + +struct fw_card *fw_card_get(struct fw_card *card); +void fw_card_put(struct fw_card *card); + +/* + * The iso packet format allows for an immediate header/payload part + * stored in 'header' immediately after the packet info plus an + * indirect payload part that is pointer to by the 'payload' field. + * Applications can use one or the other or both to implement simple + * low-bandwidth streaming (e.g. audio) or more advanced + * scatter-gather streaming (e.g. assembling video frame automatically). + */ + +struct fw_iso_packet { + u16 payload_length; /* Length of indirect payload. */ + u32 interrupt : 1; /* Generate interrupt on this packet */ + u32 skip : 1; /* Set to not send packet at all. */ + u32 tag : 2; + u32 sy : 4; + u32 header_length : 8; /* Length of immediate header. */ + u32 header[0]; +}; + +#define FW_ISO_CONTEXT_TRANSMIT 0 +#define FW_ISO_CONTEXT_RECEIVE 1 + +#define FW_ISO_CONTEXT_MATCH_TAG0 1 +#define FW_ISO_CONTEXT_MATCH_TAG1 2 +#define FW_ISO_CONTEXT_MATCH_TAG2 4 +#define FW_ISO_CONTEXT_MATCH_TAG3 8 +#define FW_ISO_CONTEXT_MATCH_ALL_TAGS 15 + +struct fw_iso_context; + +typedef void (*fw_iso_callback_t)(struct fw_iso_context *context, + u32 cycle, + size_t header_length, + void *header, + void *data); + +/* + * An iso buffer is just a set of pages mapped for DMA in the + * specified direction. Since the pages are to be used for DMA, they + * are not mapped into the kernel virtual address space. We store the + * DMA address in the page private. The helper function + * fw_iso_buffer_map() will map the pages into a given vma. + */ + +struct fw_iso_buffer { + enum dma_data_direction direction; + struct page **pages; + int page_count; +}; + +struct fw_iso_context { + struct fw_card *card; + int type; + int channel; + int speed; + size_t header_size; + fw_iso_callback_t callback; + void *callback_data; +}; + +int +fw_iso_buffer_init(struct fw_iso_buffer *buffer, + struct fw_card *card, + int page_count, + enum dma_data_direction direction); +int +fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma); +void +fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card); + +struct fw_iso_context * +fw_iso_context_create(struct fw_card *card, int type, + int channel, int speed, size_t header_size, + fw_iso_callback_t callback, void *callback_data); + +void +fw_iso_context_destroy(struct fw_iso_context *ctx); + +int +fw_iso_context_queue(struct fw_iso_context *ctx, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload); + +int +fw_iso_context_start(struct fw_iso_context *ctx, + int cycle, int sync, int tags); + +int +fw_iso_context_stop(struct fw_iso_context *ctx); + +struct fw_card_driver { + const char *name; + + /* + * Enable the given card with the given initial config rom. + * This function is expected to activate the card, and either + * enable the PHY or set the link_on bit and initiate a bus + * reset. + */ + int (*enable)(struct fw_card *card, u32 *config_rom, size_t length); + + int (*update_phy_reg)(struct fw_card *card, int address, + int clear_bits, int set_bits); + + /* + * Update the config rom for an enabled card. This function + * should change the config rom that is presented on the bus + * an initiate a bus reset. + */ + int (*set_config_rom)(struct fw_card *card, + u32 *config_rom, size_t length); + + void (*send_request)(struct fw_card *card, struct fw_packet *packet); + void (*send_response)(struct fw_card *card, struct fw_packet *packet); + /* Calling cancel is valid once a packet has been submitted. */ + int (*cancel_packet)(struct fw_card *card, struct fw_packet *packet); + + /* + * Allow the specified node ID to do direct DMA out and in of + * host memory. The card will disable this for all node when + * a bus reset happens, so driver need to reenable this after + * bus reset. Returns 0 on success, -ENODEV if the card + * doesn't support this, -ESTALE if the generation doesn't + * match. + */ + int (*enable_phys_dma)(struct fw_card *card, + int node_id, int generation); + + u64 (*get_bus_time)(struct fw_card *card); + + struct fw_iso_context * + (*allocate_iso_context)(struct fw_card *card, + int type, size_t header_size); + void (*free_iso_context)(struct fw_iso_context *ctx); + + int (*start_iso)(struct fw_iso_context *ctx, + s32 cycle, u32 sync, u32 tags); + + int (*queue_iso)(struct fw_iso_context *ctx, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload); + + int (*stop_iso)(struct fw_iso_context *ctx); +}; + +int +fw_core_initiate_bus_reset(struct fw_card *card, int short_reset); + +void +fw_send_request(struct fw_card *card, struct fw_transaction *t, + int tcode, int node_id, int generation, int speed, + unsigned long long offset, + void *data, size_t length, + fw_transaction_callback_t callback, void *callback_data); + +int fw_cancel_transaction(struct fw_card *card, + struct fw_transaction *transaction); + +void fw_flush_transactions(struct fw_card *card); + +void fw_send_phy_config(struct fw_card *card, + int node_id, int generation, int gap_count); + +/* + * Called by the topology code to inform the device code of node + * activity; found, lost, or updated nodes. + */ +void +fw_node_event(struct fw_card *card, struct fw_node *node, int event); + +/* API used by card level drivers */ + +void +fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver, + struct device *device); +int +fw_card_add(struct fw_card *card, + u32 max_receive, u32 link_speed, u64 guid); + +void +fw_core_remove_card(struct fw_card *card); + +void +fw_core_handle_bus_reset(struct fw_card *card, + int node_id, int generation, + int self_id_count, u32 *self_ids); +void +fw_core_handle_request(struct fw_card *card, struct fw_packet *request); + +void +fw_core_handle_response(struct fw_card *card, struct fw_packet *packet); + +#endif /* __fw_transaction_h */ diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index a19b65ed3119..7f817897b178 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -240,11 +240,94 @@ static inline void hidinput_pb_setup(struct input_dev *input) } #endif +static inline int match_scancode(int code, int scancode) +{ + if (scancode == 0) + return 1; + return ((code & (HID_USAGE_PAGE | HID_USAGE)) == scancode); +} + +static inline int match_keycode(int code, int keycode) +{ + if (keycode == 0) + return 1; + return (code == keycode); +} + +static struct hid_usage *hidinput_find_key(struct hid_device *hid, + int scancode, int keycode) +{ + int i, j, k; + struct hid_report *report; + struct hid_usage *usage; + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { + list_for_each_entry(report, &hid->report_enum[k].report_list, list) { + for (i = 0; i < report->maxfield; i++) { + for ( j = 0; j < report->field[i]->maxusage; j++) { + usage = report->field[i]->usage + j; + if (usage->type == EV_KEY && + match_scancode(usage->hid, scancode) && + match_keycode(usage->code, keycode)) + return usage; + } + } + } + } + return NULL; +} + +static int hidinput_getkeycode(struct input_dev *dev, int scancode, + int *keycode) +{ + struct hid_device *hid = dev->private; + struct hid_usage *usage; + + usage = hidinput_find_key(hid, scancode, 0); + if (usage) { + *keycode = usage->code; + return 0; + } + return -EINVAL; +} + +static int hidinput_setkeycode(struct input_dev *dev, int scancode, + int keycode) +{ + struct hid_device *hid = dev->private; + struct hid_usage *usage; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + usage = hidinput_find_key(hid, scancode, 0); + if (usage) { + old_keycode = usage->code; + usage->code = keycode; + + clear_bit(old_keycode, dev->keybit); + set_bit(usage->code, dev->keybit); +#ifdef CONFIG_HID_DEBUG + printk (KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode); +#endif + /* Set the keybit for the old keycode if the old keycode is used + * by another key */ + if (hidinput_find_key (hid, 0, old_keycode)) + set_bit(old_keycode, dev->keybit); + + return 0; + } + + return -EINVAL; +} + + static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage) { struct input_dev *input = hidinput->input; - struct hid_device *device = input->private; + struct hid_device *device = input_get_drvdata(input); int max = 0, code; unsigned long *bit = NULL; @@ -553,6 +636,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x1015: map_key_clear(KEY_RECORD); break; case 0x1016: map_key_clear(KEY_PLAYER); break; case 0x1017: map_key_clear(KEY_EJECTCD); break; + case 0x1018: map_key_clear(KEY_MEDIA); break; case 0x1019: map_key_clear(KEY_PROG1); break; case 0x101a: map_key_clear(KEY_PROG2); break; case 0x101b: map_key_clear(KEY_PROG3); break; @@ -560,9 +644,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x1020: map_key_clear(KEY_ZOOMOUT); break; case 0x1021: map_key_clear(KEY_ZOOMRESET); break; case 0x1023: map_key_clear(KEY_CLOSE); break; + case 0x1027: map_key_clear(KEY_MENU); break; /* this one is marked as 'Rotate' */ case 0x1028: map_key_clear(KEY_ANGLE); break; case 0x1029: map_key_clear(KEY_SHUFFLE); break; + case 0x102a: map_key_clear(KEY_BACK); break; + case 0x102b: map_key_clear(KEY_CYCLEWINDOWS); break; case 0x1041: map_key_clear(KEY_BATTERY); break; case 0x1042: map_key_clear(KEY_WORDPROCESSOR); break; case 0x1043: map_key_clear(KEY_SPREADSHEET); break; @@ -855,13 +942,15 @@ EXPORT_SYMBOL_GPL(hidinput_find_field); static int hidinput_open(struct input_dev *dev) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); + return hid->hid_open(hid); } static void hidinput_close(struct input_dev *dev) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); + hid->hid_close(hid); } @@ -909,10 +998,12 @@ int hidinput_connect(struct hid_device *hid) return -1; } - input_dev->private = hid; + input_set_drvdata(input_dev, hid); input_dev->event = hid->hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; + input_dev->setkeycode = hidinput_setkeycode; + input_dev->getkeycode = hidinput_getkeycode; input_dev->name = hid->name; input_dev->phys = hid->phys; @@ -921,7 +1012,7 @@ int hidinput_connect(struct hid_device *hid) input_dev->id.vendor = hid->vendor; input_dev->id.product = hid->product; input_dev->id.version = hid->version; - input_dev->cdev.dev = hid->dev; + input_dev->dev.parent = hid->dev; hidinput->input = input_dev; list_add_tail(&hidinput->list, &hid->inputs); } diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 7c87bdc538bc..1b4b572f899b 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -25,12 +25,12 @@ comment "Input core support is needed for USB HID input layer or HIDBP support" depends on USB_HID && INPUT=n config USB_HIDINPUT_POWERBOOK - bool "Enable support for iBook/PowerBook special keys" + bool "Enable support for iBook/PowerBook/MacBook/MacBookPro special keys" default n depends on USB_HID help Say Y here if you want support for the special keys (Fn, Numlock) on - Apple iBooks and PowerBooks. + Apple iBooks, PowerBooks, MacBooks and MacBook Pros. If unsure, say N. diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 91d610358d57..d91b9dac6dff 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -446,7 +446,7 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct hid_field *field; int offset; @@ -626,14 +626,10 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; - if (usbhid->inbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma); - if (usbhid->outbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma); - if (usbhid->cr) - usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma); - if (usbhid->ctrlbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); + usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma); + usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma); + usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma); + usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); } /* @@ -692,6 +688,30 @@ static void hid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize) } } +/* + * Some USB barcode readers from cypress have usage min and usage max in + * the wrong order + */ +static void hid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize) +{ + short fixed = 0; + int i; + + for (i = 0; i < rsize - 4; i++) { + if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) { + unsigned char tmp; + + rdesc[i] = 0x19; rdesc[i+2] = 0x29; + tmp = rdesc[i+3]; + rdesc[i+3] = rdesc[i+1]; + rdesc[i+1] = tmp; + } + } + + if (fixed) + info("Fixing up Cypress report descriptor"); +} + static struct hid_device *usb_hid_configure(struct usb_interface *intf) { struct usb_host_interface *interface = intf->cur_altsetting; @@ -758,6 +778,9 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (quirks & HID_QUIRK_LOGITECH_DESCRIPTOR) hid_fixup_logitech_descriptor(rdesc, rsize); + if (quirks & HID_QUIRK_SWAPPED_MIN_MAX) + hid_fixup_cypress_descriptor(rdesc, rsize); + #ifdef CONFIG_HID_DEBUG printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) diff --git a/drivers/hid/usbhid/hid-lgff.c b/drivers/hid/usbhid/hid-lgff.c index 92d2553f17b6..c5cd4107d6af 100644 --- a/drivers/hid/usbhid/hid-lgff.c +++ b/drivers/hid/usbhid/hid-lgff.c @@ -60,7 +60,7 @@ static const struct dev_type devices[] = { static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct hid_report *report = list_entry(report_list->next, struct hid_report, list); int x, y; diff --git a/drivers/hid/usbhid/hid-plff.c b/drivers/hid/usbhid/hid-plff.c index 76d2e6e14db4..d6a8f2b49bd2 100644 --- a/drivers/hid/usbhid/hid-plff.c +++ b/drivers/hid/usbhid/hid-plff.c @@ -37,7 +37,7 @@ struct plff_device { static int hid_plff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct plff_device *plff = data; int left, right; diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 17a87555e32f..f6c4145dc202 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -92,6 +92,8 @@ #define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 #define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 #define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 +#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61 +#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64 #define USB_VENDOR_ID_DELL 0x413c #define USB_DEVICE_ID_DELL_W7658 0x2005 @@ -193,6 +195,7 @@ #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 +#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_S510_RECEIVER 0xc50c #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 #define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 @@ -422,6 +425,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, @@ -445,6 +449,9 @@ static const struct hid_blacklist { { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_SWAPPED_MIN_MAX }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_SWAPPED_MIN_MAX }, + { 0, 0 } }; diff --git a/drivers/hid/usbhid/hid-tmff.c b/drivers/hid/usbhid/hid-tmff.c index ab67331620d0..ab5ba6ef891c 100644 --- a/drivers/hid/usbhid/hid-tmff.c +++ b/drivers/hid/usbhid/hid-tmff.c @@ -59,7 +59,7 @@ static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct tmff_device *tmff = data; int left, right; /* Rumbling */ diff --git a/drivers/hid/usbhid/hid-zpff.c b/drivers/hid/usbhid/hid-zpff.c index 7bd8238ca212..a7fbffcdaf36 100644 --- a/drivers/hid/usbhid/hid-zpff.c +++ b/drivers/hid/usbhid/hid-zpff.c @@ -37,7 +37,7 @@ struct zpff_device { static int hid_zpff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct zpff_device *zpff = data; int left, right; diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index a8b3d66cd498..488d61bdbf2c 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -51,6 +51,7 @@ struct hiddev { wait_queue_head_t wait; struct hid_device *hid; struct list_head list; + spinlock_t list_lock; }; struct hiddev_list { @@ -161,7 +162,9 @@ static void hiddev_send_event(struct hid_device *hid, { struct hiddev *hiddev = hid->hiddev; struct hiddev_list *list; + unsigned long flags; + spin_lock_irqsave(&hiddev->list_lock, flags); list_for_each_entry(list, &hiddev->list, node) { if (uref->field_index != HID_FIELD_INDEX_NONE || (list->flags & HIDDEV_FLAG_REPORT) != 0) { @@ -171,6 +174,7 @@ static void hiddev_send_event(struct hid_device *hid, kill_fasync(&list->fasync, SIGIO, POLL_IN); } } + spin_unlock_irqrestore(&hiddev->list_lock, flags); wake_up_interruptible(&hiddev->wait); } @@ -235,9 +239,13 @@ static int hiddev_fasync(int fd, struct file *file, int on) static int hiddev_release(struct inode * inode, struct file * file) { struct hiddev_list *list = file->private_data; + unsigned long flags; hiddev_fasync(-1, file, 0); + + spin_lock_irqsave(&list->hiddev->list_lock, flags); list_del(&list->node); + spin_unlock_irqrestore(&list->hiddev->list_lock, flags); if (!--list->hiddev->open) { if (list->hiddev->exist) @@ -257,6 +265,7 @@ static int hiddev_release(struct inode * inode, struct file * file) static int hiddev_open(struct inode *inode, struct file *file) { struct hiddev_list *list; + unsigned long flags; int i = iminor(inode) - HIDDEV_MINOR_BASE; @@ -267,7 +276,11 @@ static int hiddev_open(struct inode *inode, struct file *file) return -ENOMEM; list->hiddev = hiddev_table[i]; + + spin_lock_irqsave(&list->hiddev->list_lock, flags); list_add_tail(&list->node, &hiddev_table[i]->list); + spin_unlock_irqrestore(&list->hiddev->list_lock, flags); + file->private_data = list; if (!list->hiddev->open++) @@ -773,6 +786,7 @@ int hiddev_connect(struct hid_device *hid) init_waitqueue_head(&hiddev->wait); INIT_LIST_HEAD(&hiddev->list); + spin_lock_init(&hiddev->list_lock); hiddev->hid = hid; hiddev->exist = 1; diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 65aa12e8d7b3..130978780713 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -133,12 +133,11 @@ resubmit: static int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct usb_kbd *kbd = dev->private; + struct usb_kbd *kbd = input_get_drvdata(dev); if (type != EV_LED) return -1; - kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | (!!test_bit(LED_NUML, dev->led)); @@ -175,7 +174,7 @@ static void usb_kbd_led(struct urb *urb) static int usb_kbd_open(struct input_dev *dev) { - struct usb_kbd *kbd = dev->private; + struct usb_kbd *kbd = input_get_drvdata(dev); kbd->irq->dev = kbd->usbdev; if (usb_submit_urb(kbd->irq, GFP_KERNEL)) @@ -186,7 +185,7 @@ static int usb_kbd_open(struct input_dev *dev) static void usb_kbd_close(struct input_dev *dev) { - struct usb_kbd *kbd = dev->private; + struct usb_kbd *kbd = input_get_drvdata(dev); usb_kill_urb(kbd->irq); } @@ -211,12 +210,9 @@ static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd) { usb_free_urb(kbd->irq); usb_free_urb(kbd->led); - if (kbd->new) - usb_buffer_free(dev, 8, kbd->new, kbd->new_dma); - if (kbd->cr) - usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma); - if (kbd->leds) - usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); + usb_buffer_free(dev, 8, kbd->new, kbd->new_dma); + usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma); + usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); } static int usb_kbd_probe(struct usb_interface *iface, @@ -274,8 +270,9 @@ static int usb_kbd_probe(struct usb_interface *iface, input_dev->name = kbd->name; input_dev->phys = kbd->phys; usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &iface->dev; - input_dev->private = kbd; + input_dev->dev.parent = &iface->dev; + + input_set_drvdata(input_dev, kbd); input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA); diff --git a/drivers/hid/usbhid/usbmouse.c b/drivers/hid/usbhid/usbmouse.c index 573776d865e1..5345c73bcf62 100644 --- a/drivers/hid/usbhid/usbmouse.c +++ b/drivers/hid/usbhid/usbmouse.c @@ -96,7 +96,7 @@ resubmit: static int usb_mouse_open(struct input_dev *dev) { - struct usb_mouse *mouse = dev->private; + struct usb_mouse *mouse = input_get_drvdata(dev); mouse->irq->dev = mouse->usbdev; if (usb_submit_urb(mouse->irq, GFP_KERNEL)) @@ -107,7 +107,7 @@ static int usb_mouse_open(struct input_dev *dev) static void usb_mouse_close(struct input_dev *dev) { - struct usb_mouse *mouse = dev->private; + struct usb_mouse *mouse = input_get_drvdata(dev); usb_kill_urb(mouse->irq); } @@ -171,7 +171,7 @@ static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_i input_dev->name = mouse->name; input_dev->phys = mouse->phys; usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &intf->dev; + input_dev->dev.parent = &intf->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); @@ -179,7 +179,8 @@ static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_i input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); input_dev->relbit[0] |= BIT(REL_WHEEL); - input_dev->private = mouse; + input_set_drvdata(input_dev, mouse); + input_dev->open = usb_mouse_open; input_dev->close = usb_mouse_close; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3ba3a5221c41..4d1cb5b855d1 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -4,6 +4,7 @@ menuconfig HWMON tristate "Hardware Monitoring support" + depends on HAS_IOMEM default y help Hardware monitoring devices let you monitor the hardware health diff --git a/drivers/hwmon/ams/ams-input.c b/drivers/hwmon/ams/ams-input.c index 18210164e307..ca7095d96ad0 100644 --- a/drivers/hwmon/ams/ams-input.c +++ b/drivers/hwmon/ams/ams-input.c @@ -87,7 +87,7 @@ static void ams_input_enable(void) ams_info.idev->id.vendor = 0; ams_info.idev->open = ams_input_open; ams_info.idev->close = ams_input_close; - ams_info.idev->cdev.dev = &ams_info.of_dev->dev; + ams_info.idev->dev.parent = &ams_info.of_dev->dev; input_set_abs_params(ams_info.idev, ABS_X, -50, 50, 3, 0); input_set_abs_params(ams_info.idev, ABS_Y, -50, 50, 3, 0); diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index b51c104a28a2..0c160675b3ac 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -1100,7 +1100,7 @@ static int applesmc_create_accelerometer(void) /* initialize the input class */ applesmc_idev->name = "applesmc"; applesmc_idev->id.bustype = BUS_HOST; - applesmc_idev->cdev.dev = &pdev->dev; + applesmc_idev->dev.parent = &pdev->dev; applesmc_idev->evbit[0] = BIT(EV_ABS); applesmc_idev->open = applesmc_idev_open; applesmc_idev->close = applesmc_idev_close; diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c index f82fa2d23f95..e0cf5e6fe5bc 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c @@ -574,7 +574,7 @@ static int __init hdaps_init(void) /* initialize the input class */ hdaps_idev->name = "hdaps"; - hdaps_idev->cdev.dev = &pdev->dev; + hdaps_idev->dev.parent = &pdev->dev; hdaps_idev->evbit[0] = BIT(EV_ABS); input_set_abs_params(hdaps_idev, ABS_X, -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 434a61b415a3..96867347bcbf 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -4,6 +4,7 @@ menuconfig I2C tristate "I2C support" + depends on HAS_IOMEM ---help--- I2C (pronounce: I-square-C) is a slow serial bus protocol used in many micro controller applications and developed by Philips. SMBus, diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index f35156c58922..9c8b6d5eaec9 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/version.h> #include <linux/kernel.h> +#include <linux/err.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/delay.h> @@ -226,13 +227,14 @@ static int __devinit at91_i2c_probe(struct platform_device *pdev) adapter->algo = &at91_algorithm; adapter->class = I2C_CLASS_HWMON; adapter->dev.parent = &pdev->dev; + /* adapter->id == 0 ... only one TWI controller for now */ platform_set_drvdata(pdev, adapter); clk_enable(twi_clk); /* enable peripheral clock */ at91_twi_hwinit(); /* initialize TWI controller */ - rc = i2c_add_adapter(adapter); + rc = i2c_add_numbered_adapter(adapter); if (rc) { dev_err(&pdev->dev, "Adapter %s registration failed\n", adapter->name); @@ -295,6 +297,9 @@ static int at91_i2c_resume(struct platform_device *pdev) #define at91_i2c_resume NULL #endif +/* work with "modprobe at91_i2c" from hotplugging or coldplugging */ +MODULE_ALIAS("at91_i2c"); + static struct platform_driver at91_i2c_driver = { .probe = at91_i2c_probe, .remove = __devexit_p(at91_i2c_remove), diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 873544ab598e..8a0a99b93641 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -548,7 +548,7 @@ static inline void i2c_pxa_stop_message(struct pxa_i2c *i2c) */ icr = readl(_ICR(i2c)); icr &= ~(ICR_STOP | ICR_ACKNAK); - writel(icr, _IRC(i2c)); + writel(icr, _ICR(i2c)); } /* diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 1d06b415ede9..9040809d2c25 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -7,6 +7,7 @@ if BLOCK menu "ATA/ATAPI/MFM/RLL support" + depends on HAS_IOMEM config IDE tristate "ATA/ATAPI/MFM/RLL support" diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig index 61d7809a5a26..8012b3b0ce75 100644 --- a/drivers/ieee1394/Kconfig +++ b/drivers/ieee1394/Kconfig @@ -1,4 +1,7 @@ menu "IEEE 1394 (FireWire) support" + depends on PCI || BROKEN + +source "drivers/firewire/Kconfig" config IEEE1394 tristate "IEEE 1394 (FireWire) support" diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index 37deaae49190..994decc7bcf2 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -1,4 +1,5 @@ menu "InfiniBand support" + depends on HAS_IOMEM config INFINIBAND depends on PCI || BROKEN diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 0e9b69535ad6..f814fb3a469d 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -3,6 +3,7 @@ # menu "Input device support" + depends on !S390 config INPUT tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 55a72592704c..b234729706be 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -336,7 +336,7 @@ static int bits_to_user(unsigned long *bits, unsigned int maxbit, if (compat) { len = NBITS_COMPAT(maxbit) * sizeof(compat_long_t); - if (len < maxlen) + if (len > maxlen) len = maxlen; for (i = 0; i < len / sizeof(compat_long_t); i++) diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index 3d4b619dadab..e759944041ab 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -51,7 +51,7 @@ static void ixp4xx_spkr_control(unsigned int pin, unsigned int count) static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - unsigned int pin = (unsigned int) input_get_drvdata(input_dev); + unsigned int pin = (unsigned int) input_get_drvdata(dev); unsigned int count = 0; if (type != EV_SND) diff --git a/drivers/isdn/Kconfig b/drivers/isdn/Kconfig index c90afeea54aa..d42fe89cddf6 100644 --- a/drivers/isdn/Kconfig +++ b/drivers/isdn/Kconfig @@ -3,6 +3,7 @@ # menu "ISDN subsystem" + depends on !S390 config ISDN tristate "ISDN support" diff --git a/drivers/kvm/Kconfig b/drivers/kvm/Kconfig index 703cc88d1ef9..e8e37d826478 100644 --- a/drivers/kvm/Kconfig +++ b/drivers/kvm/Kconfig @@ -2,6 +2,7 @@ # KVM configuration # menu "Virtualization" + depends on X86 config KVM tristate "Kernel-based Virtual Machine (KVM) support" diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 80acd08f0e97..87d2046f866c 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -1,5 +1,6 @@ menu "LED devices" + depends on HAS_IOMEM config NEW_LEDS bool "LED Support" diff --git a/drivers/md/md.c b/drivers/md/md.c index 65814b0340cb..c10ce91b64e9 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5103,7 +5103,7 @@ static int is_mddev_idle(mddev_t *mddev) * * Note: the following is an unsigned comparison. */ - if ((curr_events - rdev->last_events + 4096) > 8192) { + if ((long)curr_events - (long)rdev->last_events > 4096) { rdev->last_events = curr_events; idle = 0; } diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 97ee870b265d..3a95cc5e029c 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -271,21 +271,25 @@ static int raid1_end_read_request(struct bio *bio, unsigned int bytes_done, int */ update_head_pos(mirror, r1_bio); - if (uptodate || (conf->raid_disks - conf->mddev->degraded) <= 1) { - /* - * Set R1BIO_Uptodate in our master bio, so that - * we will return a good error code for to the higher - * levels even if IO on some other mirrored buffer fails. - * - * The 'master' represents the composite IO operation to - * user-side. So if something waits for IO, then it will - * wait for the 'master' bio. + if (uptodate) + set_bit(R1BIO_Uptodate, &r1_bio->state); + else { + /* If all other devices have failed, we want to return + * the error upwards rather than fail the last device. + * Here we redefine "uptodate" to mean "Don't want to retry" */ - if (uptodate) - set_bit(R1BIO_Uptodate, &r1_bio->state); + unsigned long flags; + spin_lock_irqsave(&conf->device_lock, flags); + if (r1_bio->mddev->degraded == conf->raid_disks || + (r1_bio->mddev->degraded == conf->raid_disks-1 && + !test_bit(Faulty, &conf->mirrors[mirror].rdev->flags))) + uptodate = 1; + spin_unlock_irqrestore(&conf->device_lock, flags); + } + if (uptodate) raid_end_bio_io(r1_bio); - } else { + else { /* * oops, read error: */ @@ -992,13 +996,14 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev) unsigned long flags; spin_lock_irqsave(&conf->device_lock, flags); mddev->degraded++; + set_bit(Faulty, &rdev->flags); spin_unlock_irqrestore(&conf->device_lock, flags); /* * if recovery is running, make sure it aborts. */ set_bit(MD_RECOVERY_ERR, &mddev->recovery); - } - set_bit(Faulty, &rdev->flags); + } else + set_bit(Faulty, &rdev->flags); set_bit(MD_CHANGE_DEVS, &mddev->flags); printk(KERN_ALERT "raid1: Disk failure on %s, disabling device. \n" " Operation continuing on %d devices\n", diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 91d25798ae4a..3a80e0cc7369 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -3,6 +3,7 @@ # menu "Multimedia devices" + depends on HAS_IOMEM config VIDEO_DEV tristate "Video For Linux" diff --git a/drivers/message/fusion/Kconfig b/drivers/message/fusion/Kconfig index 71037f91c222..c88cc75ab49b 100644 --- a/drivers/message/fusion/Kconfig +++ b/drivers/message/fusion/Kconfig @@ -1,5 +1,6 @@ menu "Fusion MPT device support" + depends on PCI config FUSION bool diff --git a/drivers/message/i2o/Kconfig b/drivers/message/i2o/Kconfig index 6443392bffff..f4ac21e5771e 100644 --- a/drivers/message/i2o/Kconfig +++ b/drivers/message/i2o/Kconfig @@ -1,5 +1,6 @@ menu "I2O device support" + depends on PCI config I2O tristate "I2O support" diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ab6e985275b2..a20a51efe118 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -3,6 +3,7 @@ # menu "Multifunction device drivers" + depends on HAS_IOMEM config MFD_SM501 tristate "Support for Silicon Motion SM501" diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 877e7909a0e5..2f2fbffafbe0 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -130,7 +130,7 @@ config SONY_LAPTOP Read <file:Documentation/sony-laptop.txt> for more information. -config SONY_LAPTOP_OLD +config SONYPI_COMPAT bool "Sonypi compatibility" depends on SONY_LAPTOP ---help--- diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index 65c32a95e121..4f9060a2a2f2 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c @@ -30,7 +30,7 @@ * Eric Burghard - LED display support for W1N * Josh Green - Light Sens support * Thomas Tuttle - His first patch for led support was very helpfull - * + * Sam Lin - GPS support */ #include <linux/autoconf.h> @@ -48,7 +48,7 @@ #include <acpi/acpi_bus.h> #include <asm/uaccess.h> -#define ASUS_LAPTOP_VERSION "0.41" +#define ASUS_LAPTOP_VERSION "0.42" #define ASUS_HOTK_NAME "Asus Laptop Support" #define ASUS_HOTK_CLASS "hotkey" @@ -83,6 +83,7 @@ #define PLED_ON 0x20 //Phone LED #define GLED_ON 0x40 //Gaming LED #define LCD_ON 0x80 //LCD backlight +#define GPS_ON 0x100 //GPS #define ASUS_LOG ASUS_HOTK_FILE ": " #define ASUS_ERR KERN_ERR ASUS_LOG @@ -148,7 +149,7 @@ ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP"); ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD", /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */ "\\_SB.PCI0.P0P2.VGA.GETD", /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V - S5A M5A z33A W1Jc W2V */ + S5A M5A z33A W1Jc W2V G1 */ "\\_SB.PCI0.P0P3.VGA.GETD", /* A6V A6Q */ "\\_SB.PCI0.P0PA.VGA.GETD", /* A6T, A6M */ "\\_SB.PCI0.PCI1.VGAC.NMAP", /* L3C */ @@ -162,6 +163,12 @@ ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD", /* A6B, A6K A6R A7D F3JM L ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */ ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */ +/* GPS */ +/* R2H use different handle for GPS on/off */ +ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON"); /* R2H */ +ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */ +ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST"); + /* * This is the main structure, we can use it to store anything interesting * about the hotk device @@ -278,12 +285,28 @@ static int read_wireless_status(int mask) return (hotk->status & mask) ? 1 : 0; } +static int read_gps_status(void) +{ + ulong status; + acpi_status rv = AE_OK; + + rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status); + if (ACPI_FAILURE(rv)) + printk(ASUS_WARNING "Error reading GPS status\n"); + else + return status ? 1 : 0; + + return (hotk->status & GPS_ON) ? 1 : 0; +} + /* Generic LED functions */ static int read_status(int mask) { /* There is a special method for both wireless devices */ if (mask == BT_ON || mask == WL_ON) return read_wireless_status(mask); + else if (mask == GPS_ON) + return read_gps_status(); return (hotk->status & mask) ? 1 : 0; } @@ -299,6 +322,10 @@ static void write_status(acpi_handle handle, int out, int mask) case GLED_ON: out = (out & 0x1) + 1; break; + case GPS_ON: + handle = (out) ? gps_on_handle : gps_off_handle; + out = 0x02; + break; default: out &= 0x1; break; @@ -667,6 +694,21 @@ static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr, return rv; } +/* + * GPS + */ +static ssize_t show_gps(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", read_status(GPS_ON)); +} + +static ssize_t store_gps(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return store_status(buf, count, NULL, GPS_ON); +} + static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) { /* TODO Find a better way to handle events count. */ @@ -715,6 +757,7 @@ static ASUS_CREATE_DEVICE_ATTR(display); static ASUS_CREATE_DEVICE_ATTR(ledd); static ASUS_CREATE_DEVICE_ATTR(ls_switch); static ASUS_CREATE_DEVICE_ATTR(ls_level); +static ASUS_CREATE_DEVICE_ATTR(gps); static struct attribute *asuspf_attributes[] = { &dev_attr_infos.attr, @@ -724,6 +767,7 @@ static struct attribute *asuspf_attributes[] = { &dev_attr_ledd.attr, &dev_attr_ls_switch.attr, &dev_attr_ls_level.attr, + &dev_attr_gps.attr, NULL }; @@ -763,6 +807,9 @@ static void asus_hotk_add_fs(void) ASUS_SET_DEVICE_ATTR(ls_level, 0644, show_lslvl, store_lslvl); ASUS_SET_DEVICE_ATTR(ls_switch, 0644, show_lssw, store_lssw); } + + if (gps_status_handle && gps_on_handle && gps_off_handle) + ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps); } static int asus_handle_init(char *name, acpi_handle * handle, @@ -890,9 +937,13 @@ static int asus_hotk_get_info(void) /* There is a lot of models with "ALSL", but a few get a real light sens, so we need to check it. */ - if (ASUS_HANDLE_INIT(ls_switch)) + if (!ASUS_HANDLE_INIT(ls_switch)) ASUS_HANDLE_INIT(ls_level); + ASUS_HANDLE_INIT(gps_on); + ASUS_HANDLE_INIT(gps_off); + ASUS_HANDLE_INIT(gps_status); + kfree(model); return AE_OK; @@ -950,7 +1001,7 @@ static int asus_hotk_add(struct acpi_device *device) * We install the handler, it will receive the hotk in parameter, so, we * could add other data to the hotk struct */ - status = acpi_install_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY, + status = acpi_install_notify_handler(hotk->handle, ACPI_ALL_NOTIFY, asus_hotk_notify, hotk); if (ACPI_FAILURE(status)) printk(ASUS_ERR "Error installing notify handler\n"); @@ -981,6 +1032,9 @@ static int asus_hotk_add(struct acpi_device *device) if (ls_level_handle) set_light_sens_level(hotk->light_level); + /* GPS is on by default */ + write_status(NULL, 1, GPS_ON); + end: if (result) { kfree(hotk->name); @@ -997,7 +1051,7 @@ static int asus_hotk_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; - status = acpi_remove_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY, + status = acpi_remove_notify_handler(hotk->handle, ACPI_ALL_NOTIFY, asus_hotk_notify); if (ACPI_FAILURE(status)) printk(ASUS_ERR "Error removing notify handler\n"); diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c index 68c4b58525ba..41e901f53e7c 100644 --- a/drivers/misc/msi-laptop.c +++ b/drivers/misc/msi-laptop.c @@ -85,7 +85,7 @@ static int set_lcd_level(int level) buf[0] = 0x80; buf[1] = (u8) (level*31); - return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0); + return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1); } static int get_lcd_level(void) @@ -93,7 +93,7 @@ static int get_lcd_level(void) u8 wdata = 0, rdata; int result; - result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1); + result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1); if (result < 0) return result; @@ -105,7 +105,7 @@ static int get_auto_brightness(void) u8 wdata = 4, rdata; int result; - result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1); + result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1); if (result < 0) return result; @@ -119,14 +119,14 @@ static int set_auto_brightness(int enable) wdata[0] = 4; - result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1); + result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1); if (result < 0) return result; wdata[0] = 0x84; wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); - return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0); + return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1); } static int get_wireless_state(int *wlan, int *bluetooth) @@ -134,7 +134,7 @@ static int get_wireless_state(int *wlan, int *bluetooth) u8 wdata = 0, rdata; int result; - result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1); + result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1); if (result < 0) return -1; diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index c15c1f61bd1b..8ee0321ef1c8 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c @@ -63,7 +63,7 @@ #include <asm/uaccess.h> #include <linux/sonypi.h> #include <linux/sony-laptop.h> -#ifdef CONFIG_SONY_LAPTOP_OLD +#ifdef CONFIG_SONYPI_COMPAT #include <linux/poll.h> #include <linux/miscdevice.h> #endif @@ -114,7 +114,7 @@ MODULE_PARM_DESC(camera, "set this to 1 to enable Motion Eye camera controls " "(only use it if you have a C1VE or C1VN model)"); -#ifdef CONFIG_SONY_LAPTOP_OLD +#ifdef CONFIG_SONYPI_COMPAT static int minor = -1; module_param(minor, int, 0); MODULE_PARM_DESC(minor, @@ -1504,7 +1504,7 @@ static struct attribute_group spic_attribute_group = { }; /******** SONYPI compatibility **********/ -#ifdef CONFIG_SONY_LAPTOP_OLD +#ifdef CONFIG_SONYPI_COMPAT /* battery / brightness / temperature addresses */ #define SONYPI_BAT_FLAGS 0x81 @@ -1798,7 +1798,7 @@ static void sonypi_compat_exit(void) static int sonypi_compat_init(void) { return 0; } static void sonypi_compat_exit(void) { } static void sonypi_compat_report_event(u8 event) { } -#endif /* CONFIG_SONY_LAPTOP_OLD */ +#endif /* CONFIG_SONYPI_COMPAT */ /* * ACPI callbacks diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 45b7d53b949c..c0b41e8bcd9d 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -4,6 +4,7 @@ menuconfig MMC tristate "MMC/SD card support" + depends on HAS_IOMEM help MMC is the "multi-media card" bus protocol. diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index c1b47db29bd2..fbec8cd55e38 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -2,6 +2,7 @@ menuconfig MTD tristate "Memory Technology Device (MTD) support" + depends on HAS_IOMEM help Memory Technology Devices are flash, RAM and similar chips, often used for solid state file systems on embedded devices. This option diff --git a/drivers/net/fs_enet/mac-scc.c b/drivers/net/fs_enet/mac-scc.c index d0f28981b55a..7540966687ec 100644 --- a/drivers/net/fs_enet/mac-scc.c +++ b/drivers/net/fs_enet/mac-scc.c @@ -167,7 +167,7 @@ static int allocate_bd(struct net_device *dev) fep->ring_mem_addr = cpm_dpalloc((fpi->tx_ring + fpi->rx_ring) * sizeof(cbd_t), 8); - if (IS_DPERR(fep->ring_mem_addr)) + if (IS_ERR_VALUE(fep->ring_mem_addr)) return -ENOMEM; fep->ring_base = cpm_dpram_addr(fep->ring_mem_addr); diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig index 7c8ccc09b601..829da9a1d113 100644 --- a/drivers/net/irda/Kconfig +++ b/drivers/net/irda/Kconfig @@ -141,6 +141,20 @@ config ACT200L_DONGLE To activate support for ACTiSYS IR-200L dongle you will have to start irattach like this: "irattach -d act200l". +config KINGSUN_DONGLE + tristate "KingSun/DonShine DS-620 IrDA-USB dongle" + depends on IRDA && USB && EXPERIMENTAL + help + Say Y or M here if you want to build support for the KingSun/DonShine + DS-620 IrDA-USB bridge device driver. + + This USB bridge does not conform to the IrDA-USB device class + specification, and therefore needs its own specific driver. This + dongle supports SIR speed only (9600 bps). + + To compile it as a module, choose M here: the module will be called + kingsun-sir. + comment "Old SIR device drivers" config IRPORT_SIR diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile index 5be09f1b9ee2..233a2f923730 100644 --- a/drivers/net/irda/Makefile +++ b/drivers/net/irda/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o obj-$(CONFIG_TOIM3232_DONGLE) += toim3232-sir.o +obj-$(CONFIG_KINGSUN_DONGLE) += kingsun-sir.o # The SIR helper module sir-dev-objs := sir_dev.o sir_dongle.o diff --git a/drivers/net/irda/kingsun-sir.c b/drivers/net/irda/kingsun-sir.c new file mode 100644 index 000000000000..217429122e79 --- /dev/null +++ b/drivers/net/irda/kingsun-sir.c @@ -0,0 +1,657 @@ +/***************************************************************************** +* +* Filename: kingsun-sir.c +* Version: 0.1.1 +* Description: Irda KingSun/DonShine USB Dongle +* Status: Experimental +* Author: Alex Villac�s Lasso <a_villacis@palosanto.com> +* +* Based on stir4200 and mcs7780 drivers, with (strange?) differences +* +* 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +/* + * This is my current (2007-04-25) understanding of how this dongle is supposed + * to work. This is based on reverse-engineering and examination of the packet + * data sent and received by the WinXP driver using USBSnoopy. Feel free to + * update here as more of this dongle is known: + * + * General: Unlike the other USB IrDA dongles, this particular dongle exposes, + * not two bulk (in and out) endpoints, but two *interrupt* ones. This dongle, + * like the bulk based ones (stir4200.c and mcs7780.c), requires polling in + * order to receive data. + * Transmission: Just like stir4200, this dongle uses a raw stream of data, + * which needs to be wrapped and escaped in a similar way as in stir4200.c. + * Reception: Poll-based, as in stir4200. Each read returns the contents of a + * 8-byte buffer, of which the first byte (LSB) indicates the number of bytes + * (1-7) of valid data contained within the remaining 7 bytes. For example, if + * the buffer had the following contents: + * 06 ff ff ff c0 01 04 aa + * This means that (06) there are 6 bytes of valid data. The byte 0xaa at the + * end is garbage (left over from a previous reception) and is discarded. + * If a read returns an "impossible" value as the length of valid data (such as + * 0x36) in the first byte, then the buffer is uninitialized (as is the case of + * first plug-in) and its contents should be discarded. There is currently no + * evidence that the top 5 bits of the 1st byte of the buffer can have values + * other than 0 once reception begins. + * Once valid bytes are collected, the assembled stream is a sequence of + * wrapped IrDA frames that is unwrapped and unescaped as in stir4200.c. + * BIG FAT WARNING: the dongle does *not* reset the RX buffer in any way after + * a successful read from the host, which means that in absence of further + * reception, repeated reads from the dongle will return the exact same + * contents repeatedly. Attempts to be smart and cache a previous read seem + * to result in corrupted packets, so this driver depends on the unwrap logic + * to sort out any repeated reads. + * Speed change: no commands observed so far to change speed, assumed fixed + * 9600bps (SIR). + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kref.h> +#include <linux/usb.h> +#include <linux/device.h> +#include <linux/crc32.h> + +#include <asm/unaligned.h> +#include <asm/byteorder.h> +#include <asm/uaccess.h> + +#include <net/irda/irda.h> +#include <net/irda/wrapper.h> +#include <net/irda/crc.h> + +/* + * According to lsusb, 0x07c0 is assigned to + * "Code Mercenaries Hard- und Software GmbH" + */ +#define KING_VENDOR_ID 0x07c0 +#define KING_PRODUCT_ID 0x4200 + +/* These are the currently known USB ids */ +static struct usb_device_id dongles[] = { + /* KingSun Co,Ltd IrDA/USB Bridge */ + { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, dongles); + +#define KINGSUN_MTT 0x07 + +#define KINGSUN_FIFO_SIZE 4096 +#define KINGSUN_EP_IN 0 +#define KINGSUN_EP_OUT 1 + +struct kingsun_cb { + struct usb_device *usbdev; /* init: probe_irda */ + struct net_device *netdev; /* network layer */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct net_device_stats stats; /* network statistics */ + struct qos_info qos; + + __u8 *in_buf; /* receive buffer */ + __u8 *out_buf; /* transmit buffer */ + __u8 max_rx; /* max. atomic read from dongle + (usually 8), also size of in_buf */ + __u8 max_tx; /* max. atomic write to dongle + (usually 8) */ + + iobuff_t rx_buff; /* receive unwrap state machine */ + struct timeval rx_time; + spinlock_t lock; + int receiving; + + __u8 ep_in; + __u8 ep_out; + + struct urb *tx_urb; + struct urb *rx_urb; +}; + +/* Callback transmission routine */ +static void kingsun_send_irq(struct urb *urb) +{ + struct kingsun_cb *kingsun = urb->context; + struct net_device *netdev = kingsun->netdev; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + err("kingsun_send_irq: Network not running!"); + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + err("kingsun_send_irq: urb asynchronously failed - %d", + urb->status); + } + netif_wake_queue(netdev); +} + +/* + * Called from net/core when new frame is available. + */ +static int kingsun_hard_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct kingsun_cb *kingsun; + int wraplen; + int ret = 0; + + if (skb == NULL || netdev == NULL) + return -EINVAL; + + netif_stop_queue(netdev); + + /* the IRDA wrapping routines don't deal with non linear skb */ + SKB_LINEAR_ASSERT(skb); + + kingsun = netdev_priv(netdev); + + spin_lock(&kingsun->lock); + + /* Append data to the end of whatever data remains to be transmitted */ + wraplen = async_wrap_skb(skb, + kingsun->out_buf, + KINGSUN_FIFO_SIZE); + + /* Calculate how much data can be transmitted in this urb */ + usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev, + usb_sndintpipe(kingsun->usbdev, kingsun->ep_out), + kingsun->out_buf, wraplen, kingsun_send_irq, + kingsun, 1); + + if ((ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC))) { + err("kingsun_hard_xmit: failed tx_urb submit: %d", ret); + switch (ret) { + case -ENODEV: + case -EPIPE: + break; + default: + kingsun->stats.tx_errors++; + netif_start_queue(netdev); + } + } else { + kingsun->stats.tx_packets++; + kingsun->stats.tx_bytes += skb->len; + } + + dev_kfree_skb(skb); + spin_unlock(&kingsun->lock); + + return ret; +} + +/* Receive callback function */ +static void kingsun_rcv_irq(struct urb *urb) +{ + struct kingsun_cb *kingsun = urb->context; + int ret; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + kingsun->receiving = 0; + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + err("kingsun_rcv_irq: urb asynchronously failed - %d", + urb->status); + kingsun->receiving = 0; + return; + } + + if (urb->actual_length == kingsun->max_rx) { + __u8 *bytes = urb->transfer_buffer; + int i; + + /* The very first byte in the buffer indicates the length of + valid data in the read. This byte must be in the range + 1..kingsun->max_rx -1 . Values outside this range indicate + an uninitialized Rx buffer when the dongle has just been + plugged in. */ + if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) { + for (i = 1; i <= bytes[0]; i++) { + async_unwrap_char(kingsun->netdev, + &kingsun->stats, + &kingsun->rx_buff, bytes[i]); + } + kingsun->netdev->last_rx = jiffies; + do_gettimeofday(&kingsun->rx_time); + kingsun->receiving = + (kingsun->rx_buff.state != OUTSIDE_FRAME) + ? 1 : 0; + } + } else if (urb->actual_length > 0) { + err("%s(): Unexpected response length, expected %d got %d", + __FUNCTION__, kingsun->max_rx, urb->actual_length); + } + /* This urb has already been filled in kingsun_net_open */ + ret = usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * Function kingsun_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + */ +static int kingsun_net_open(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + int err = -ENOMEM; + char hwname[16]; + + /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */ + kingsun->receiving = 0; + + /* Initialize for SIR to copy data directly into skb. */ + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->rx_buff.truesize = IRDA_SKB_MAX_MTU; + kingsun->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!kingsun->rx_buff.skb) + goto free_mem; + + skb_reserve(kingsun->rx_buff.skb, 1); + kingsun->rx_buff.head = kingsun->rx_buff.skb->data; + do_gettimeofday(&kingsun->rx_time); + + kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->rx_urb) + goto free_mem; + + kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->tx_urb) + goto free_mem; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + */ + sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); + kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); + if (!kingsun->irlap) { + err("kingsun-sir: irlap_open failed"); + goto free_mem; + } + + /* Start first reception */ + usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev, + usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in), + kingsun->in_buf, kingsun->max_rx, + kingsun_rcv_irq, kingsun, 1); + kingsun->rx_urb->status = 0; + err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + if (err) { + err("kingsun-sir: first urb-submit failed: %d", err); + goto close_irlap; + } + + netif_start_queue(netdev); + + /* Situation at this point: + - all work buffers allocated + - urbs allocated and ready to fill + - max rx packet known (in max_rx) + - unwrap state machine initialized, in state outside of any frame + - receive request in progress + - IrLAP layer started, about to hand over packets to send + */ + + return 0; + + close_irlap: + irlap_close(kingsun->irlap); + free_mem: + if (kingsun->tx_urb) { + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + } + if (kingsun->rx_urb) { + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + } + if (kingsun->rx_buff.skb) { + kfree_skb(kingsun->rx_buff.skb); + kingsun->rx_buff.skb = NULL; + kingsun->rx_buff.head = NULL; + } + return err; +} + +/* + * Function kingsun_net_close (kingsun) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int kingsun_net_close(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + + /* Stop transmit processing */ + netif_stop_queue(netdev); + + /* Mop up receive && transmit urb's */ + usb_kill_urb(kingsun->tx_urb); + usb_kill_urb(kingsun->rx_urb); + + usb_free_urb(kingsun->tx_urb); + usb_free_urb(kingsun->rx_urb); + + kingsun->tx_urb = NULL; + kingsun->rx_urb = NULL; + + kfree_skb(kingsun->rx_buff.skb); + kingsun->rx_buff.skb = NULL; + kingsun->rx_buff.head = NULL; + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->receiving = 0; + + /* Stop and remove instance of IrLAP */ + if (kingsun->irlap) + irlap_close(kingsun->irlap); + + kingsun->irlap = NULL; + + return 0; +} + +/* + * IOCTLs : Extra out-of-band network commands... + */ +static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq, + int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct kingsun_cb *kingsun = netdev_priv(netdev); + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the device is still there */ + if (netif_device_present(kingsun->netdev)) + /* No observed commands for speed change */ + ret = -EOPNOTSUPP; + break; + + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the IrDA stack is still there */ + if (netif_running(kingsun->netdev)) + irda_device_set_media_busy(kingsun->netdev, TRUE); + break; + + case SIOCGRECEIVING: + /* Only approximately true */ + irq->ifr_receiving = kingsun->receiving; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* + * Get device stats (for /proc/net/dev and ifconfig) + */ +static struct net_device_stats * +kingsun_net_get_stats(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + return &kingsun->stats; +} + +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + */ +static int kingsun_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + + struct usb_device *dev = interface_to_usbdev(intf); + struct kingsun_cb *kingsun = NULL; + struct net_device *net = NULL; + int ret = -ENOMEM; + int pipe, maxp_in, maxp_out; + __u8 ep_in; + __u8 ep_out; + + /* Check that there really are two interrupt endpoints. + Check based on the one in drivers/usb/input/usbmouse.c + */ + interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints != 2) { + err("kingsun-sir: expected 2 endpoints, found %d", + interface->desc.bNumEndpoints); + return -ENODEV; + } + endpoint = &interface->endpoint[KINGSUN_EP_IN].desc; + if (!usb_endpoint_is_int_in(endpoint)) { + err("kingsun-sir: endpoint 0 is not interrupt IN"); + return -ENODEV; + } + + ep_in = endpoint->bEndpointAddress; + pipe = usb_rcvintpipe(dev, ep_in); + maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + if (maxp_in > 255 || maxp_in <= 1) { + err("%s: endpoint 0 has max packet size %d not in range", + __FILE__, maxp_in); + return -ENODEV; + } + + endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc; + if (!usb_endpoint_is_int_out(endpoint)) { + err("kingsun-sir: endpoint 1 is not interrupt OUT"); + return -ENODEV; + } + + ep_out = endpoint->bEndpointAddress; + pipe = usb_sndintpipe(dev, ep_out); + maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + /* Allocate network device container. */ + net = alloc_irdadev(sizeof(*kingsun)); + if(!net) + goto err_out1; + + SET_MODULE_OWNER(net); + SET_NETDEV_DEV(net, &intf->dev); + kingsun = netdev_priv(net); + kingsun->irlap = NULL; + kingsun->tx_urb = NULL; + kingsun->rx_urb = NULL; + kingsun->ep_in = ep_in; + kingsun->ep_out = ep_out; + kingsun->in_buf = NULL; + kingsun->out_buf = NULL; + kingsun->max_rx = (__u8)maxp_in; + kingsun->max_tx = (__u8)maxp_out; + kingsun->netdev = net; + kingsun->usbdev = dev; + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->rx_buff.skb = NULL; + kingsun->receiving = 0; + spin_lock_init(&kingsun->lock); + + /* Allocate input buffer */ + kingsun->in_buf = (__u8 *)kmalloc(kingsun->max_rx, GFP_KERNEL); + if (!kingsun->in_buf) + goto free_mem; + + /* Allocate output buffer */ + kingsun->out_buf = (__u8 *)kmalloc(KINGSUN_FIFO_SIZE, GFP_KERNEL); + if (!kingsun->out_buf) + goto free_mem; + + printk(KERN_INFO "KingSun/DonShine IRDA/USB found at address %d, " + "Vendor: %x, Product: %x\n", + dev->devnum, le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&kingsun->qos); + + /* That's the Rx capability. */ + kingsun->qos.baud_rate.bits &= IR_9600; + kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; + irda_qos_bits_to_value(&kingsun->qos); + + /* Override the network functions we need to use */ + net->hard_start_xmit = kingsun_hard_xmit; + net->open = kingsun_net_open; + net->stop = kingsun_net_close; + net->get_stats = kingsun_net_get_stats; + net->do_ioctl = kingsun_net_ioctl; + + ret = register_netdev(net); + if (ret != 0) + goto free_mem; + + info("IrDA: Registered KingSun/DonShine device %s", net->name); + + usb_set_intfdata(intf, kingsun); + + /* Situation at this point: + - all work buffers allocated + - urbs not allocated, set to NULL + - max rx packet known (in max_rx) + - unwrap state machine (partially) initialized, but skb == NULL + */ + + return 0; + +free_mem: + if (kingsun->out_buf) kfree(kingsun->out_buf); + if (kingsun->in_buf) kfree(kingsun->in_buf); + free_netdev(net); +err_out1: + return ret; +} + +/* + * The current device is removed, the USB layer tell us to shut it down... + */ +static void kingsun_disconnect(struct usb_interface *intf) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + if (!kingsun) + return; + + unregister_netdev(kingsun->netdev); + + /* Mop up receive && transmit urb's */ + if (kingsun->tx_urb != NULL) { + usb_kill_urb(kingsun->tx_urb); + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + } + if (kingsun->rx_urb != NULL) { + usb_kill_urb(kingsun->rx_urb); + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + } + + kfree(kingsun->out_buf); + kfree(kingsun->in_buf); + free_netdev(kingsun->netdev); + + usb_set_intfdata(intf, NULL); +} + +#ifdef CONFIG_PM +/* USB suspend, so power off the transmitter/receiver */ +static int kingsun_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + netif_device_detach(kingsun->netdev); + if (kingsun->tx_urb != NULL) usb_kill_urb(kingsun->tx_urb); + if (kingsun->rx_urb != NULL) usb_kill_urb(kingsun->rx_urb); + return 0; +} + +/* Coming out of suspend, so reset hardware */ +static int kingsun_resume(struct usb_interface *intf) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + if (kingsun->rx_urb != NULL) + usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + netif_device_attach(kingsun->netdev); + + return 0; +} +#endif + +/* + * USB device callbacks + */ +static struct usb_driver irda_driver = { + .name = "kingsun-sir", + .probe = kingsun_probe, + .disconnect = kingsun_disconnect, + .id_table = dongles, +#ifdef CONFIG_PM + .suspend = kingsun_suspend, + .resume = kingsun_resume, +#endif +}; + +/* + * Module insertion + */ +static int __init kingsun_init(void) +{ + return usb_register(&irda_driver); +} +module_init(kingsun_init); + +/* + * Module removal + */ +static void __exit kingsun_cleanup(void) +{ + /* Deregister the driver and remove all pending instances */ + usb_deregister(&irda_driver); +} +module_exit(kingsun_cleanup); + +MODULE_AUTHOR("Alex Villac�s Lasso <a_villacis@palosanto.com>"); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun/DonShine"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index f994f129f3d8..c0d3101eb6a0 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -3,6 +3,7 @@ # menu "PHY device support" + depends on !S390 config PHYLIB tristate "PHY Device support and infrastructure" diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c index 1fc77300b055..2106becf6990 100644 --- a/drivers/net/sgiseeq.c +++ b/drivers/net/sgiseeq.c @@ -16,11 +16,13 @@ #include <linux/string.h> #include <linux/delay.h> #include <linux/netdevice.h> +#include <linux/platform_device.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <asm/sgi/hpc3.h> #include <asm/sgi/ip22.h> +#include <asm/sgi/seeq.h> #include "sgiseeq.h" @@ -92,13 +94,9 @@ struct sgiseeq_private { struct net_device_stats stats; - struct net_device *next_module; spinlock_t tx_lock; }; -/* A list of all installed seeq devices, for removing the driver module. */ -static struct net_device *root_sgiseeq_dev; - static inline void hpc3_eth_reset(struct hpc3_ethregs *hregs) { hregs->reset = HPC3_ERST_CRESET | HPC3_ERST_CLRIRQ; @@ -624,9 +622,12 @@ static inline void setup_rx_ring(struct sgiseeq_rx_desc *buf, int nbufs) #define ALIGNED(x) ((((unsigned long)(x)) + 0xf) & ~(0xf)) -static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq, int has_eeprom) +static int __init sgiseeq_probe(struct platform_device *pdev) { + struct sgiseeq_platform_data *pd = pdev->dev.platform_data; + struct hpc3_regs *hpcregs = pd->hpc; struct sgiseeq_init_block *sr; + unsigned int irq = pd->irq; struct sgiseeq_private *sp; struct net_device *dev; int err, i; @@ -637,6 +638,8 @@ static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq, int has_eeprom) err = -ENOMEM; goto err_out; } + + platform_set_drvdata(pdev, dev); sp = netdev_priv(dev); /* Make private data page aligned */ @@ -648,15 +651,7 @@ static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq, int has_eeprom) } sp->srings = sr; -#define EADDR_NVOFS 250 - for (i = 0; i < 3; i++) { - unsigned short tmp = has_eeprom ? - ip22_eeprom_read(&hpcregs->eeprom, EADDR_NVOFS / 2+i) : - ip22_nvram_read(EADDR_NVOFS / 2+i); - - dev->dev_addr[2 * i] = tmp >> 8; - dev->dev_addr[2 * i + 1] = tmp & 0xff; - } + memcpy(dev->dev_addr, pd->mac, ETH_ALEN); #ifdef DEBUG gpriv = sp; @@ -720,9 +715,6 @@ static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq, int has_eeprom) for (i = 0; i < 6; i++) printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); - sp->next_module = root_sgiseeq_dev; - root_sgiseeq_dev = dev; - return 0; err_out_free_page: @@ -734,43 +726,42 @@ err_out: return err; } -static int __init sgiseeq_probe(void) +static void __exit sgiseeq_remove(struct platform_device *pdev) { - unsigned int tmp, ret1, ret2 = 0; - - /* On board adapter on 1st HPC is always present */ - ret1 = sgiseeq_init(hpc3c0, SGI_ENET_IRQ, 0); - /* Let's see if second HPC is there */ - if (!(ip22_is_fullhouse()) && - get_dbe(tmp, (unsigned int *)&hpc3c1->pbdma[1]) == 0) { - sgimc->giopar |= SGIMC_GIOPAR_MASTEREXP1 | - SGIMC_GIOPAR_EXP164 | - SGIMC_GIOPAR_HPC264; - hpc3c1->pbus_piocfg[0][0] = 0x3ffff; - /* interrupt/config register on Challenge S Mezz board */ - hpc3c1->pbus_extregs[0][0] = 0x30; - ret2 = sgiseeq_init(hpc3c1, SGI_GIO_0_IRQ, 1); - } + struct net_device *dev = platform_get_drvdata(pdev); + struct sgiseeq_private *sp = netdev_priv(dev); - return (ret1 & ret2) ? ret1 : 0; + unregister_netdev(dev); + free_page((unsigned long) sp->srings); + free_netdev(dev); + platform_set_drvdata(pdev, NULL); } -static void __exit sgiseeq_exit(void) -{ - struct net_device *next, *dev; - struct sgiseeq_private *sp; +static struct platform_driver sgiseeq_driver = { + .probe = sgiseeq_probe, + .remove = __devexit_p(sgiseeq_remove), + .driver = { + .name = "sgiseeq" + } +}; - for (dev = root_sgiseeq_dev; dev; dev = next) { - sp = (struct sgiseeq_private *) netdev_priv(dev); - next = sp->next_module; - unregister_netdev(dev); - free_page((unsigned long) sp->srings); - free_netdev(dev); +static int __init sgiseeq_module_init(void) +{ + if (platform_driver_register(&sgiseeq_driver)) { + printk(KERN_ERR "Driver registration failed\n"); + return -ENODEV; } + + return 0; +} + +static void __exit sgiseeq_module_exit(void) +{ + platform_driver_unregister(&sgiseeq_driver); } -module_init(sgiseeq_probe); -module_exit(sgiseeq_exit); +module_init(sgiseeq_module_init); +module_exit(sgiseeq_module_exit); MODULE_DESCRIPTION("SGI Seeq 8003 driver"); MODULE_AUTHOR("Linux/MIPS Mailing List <linux-mips@linux-mips.org>"); diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index d7aff8189377..0f667652fda9 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -293,7 +293,7 @@ static int fill_init_enet_entries(struct ucc_geth_private *ugeth, else { init_enet_offset = qe_muram_alloc(thread_size, thread_alignment); - if (IS_MURAM_ERR(init_enet_offset)) { + if (IS_ERR_VALUE(init_enet_offset)) { ugeth_err ("fill_init_enet_entries: Can not allocate DPRAM memory."); qe_put_snum((u8) snum); @@ -2594,7 +2594,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ugeth->tx_bd_ring_offset[j] = qe_muram_alloc(length, UCC_GETH_TX_BD_RING_ALIGNMENT); - if (!IS_MURAM_ERR(ugeth->tx_bd_ring_offset[j])) + if (!IS_ERR_VALUE(ugeth->tx_bd_ring_offset[j])) ugeth->p_tx_bd_ring[j] = (u8 *) qe_muram_addr(ugeth-> tx_bd_ring_offset[j]); @@ -2629,7 +2629,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ugeth->rx_bd_ring_offset[j] = qe_muram_alloc(length, UCC_GETH_RX_BD_RING_ALIGNMENT); - if (!IS_MURAM_ERR(ugeth->rx_bd_ring_offset[j])) + if (!IS_ERR_VALUE(ugeth->rx_bd_ring_offset[j])) ugeth->p_rx_bd_ring[j] = (u8 *) qe_muram_addr(ugeth-> rx_bd_ring_offset[j]); @@ -2713,7 +2713,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ugeth->tx_glbl_pram_offset = qe_muram_alloc(sizeof(struct ucc_geth_tx_global_pram), UCC_GETH_TX_GLOBAL_PRAM_ALIGNMENT); - if (IS_MURAM_ERR(ugeth->tx_glbl_pram_offset)) { + if (IS_ERR_VALUE(ugeth->tx_glbl_pram_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for p_tx_glbl_pram.", __FUNCTION__); @@ -2735,7 +2735,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) sizeof(struct ucc_geth_thread_data_tx) + 32 * (numThreadsTxNumerical == 1), UCC_GETH_THREAD_DATA_ALIGNMENT); - if (IS_MURAM_ERR(ugeth->thread_dat_tx_offset)) { + if (IS_ERR_VALUE(ugeth->thread_dat_tx_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for p_thread_data_tx.", __FUNCTION__); @@ -2763,7 +2763,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) qe_muram_alloc(ug_info->numQueuesTx * sizeof(struct ucc_geth_send_queue_qd), UCC_GETH_SEND_QUEUE_QUEUE_DESCRIPTOR_ALIGNMENT); - if (IS_MURAM_ERR(ugeth->send_q_mem_reg_offset)) { + if (IS_ERR_VALUE(ugeth->send_q_mem_reg_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for p_send_q_mem_reg.", __FUNCTION__); @@ -2806,7 +2806,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ugeth->scheduler_offset = qe_muram_alloc(sizeof(struct ucc_geth_scheduler), UCC_GETH_SCHEDULER_ALIGNMENT); - if (IS_MURAM_ERR(ugeth->scheduler_offset)) { + if (IS_ERR_VALUE(ugeth->scheduler_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for p_scheduler.", __FUNCTION__); @@ -2854,7 +2854,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) qe_muram_alloc(sizeof (struct ucc_geth_tx_firmware_statistics_pram), UCC_GETH_TX_STATISTICS_ALIGNMENT); - if (IS_MURAM_ERR(ugeth->tx_fw_statistics_pram_offset)) { + if (IS_ERR_VALUE(ugeth->tx_fw_statistics_pram_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for" " p_tx_fw_statistics_pram.", __FUNCTION__); @@ -2893,7 +2893,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ugeth->rx_glbl_pram_offset = qe_muram_alloc(sizeof(struct ucc_geth_rx_global_pram), UCC_GETH_RX_GLOBAL_PRAM_ALIGNMENT); - if (IS_MURAM_ERR(ugeth->rx_glbl_pram_offset)) { + if (IS_ERR_VALUE(ugeth->rx_glbl_pram_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for p_rx_glbl_pram.", __FUNCTION__); @@ -2914,7 +2914,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) qe_muram_alloc(numThreadsRxNumerical * sizeof(struct ucc_geth_thread_data_rx), UCC_GETH_THREAD_DATA_ALIGNMENT); - if (IS_MURAM_ERR(ugeth->thread_dat_rx_offset)) { + if (IS_ERR_VALUE(ugeth->thread_dat_rx_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for p_thread_data_rx.", __FUNCTION__); @@ -2937,7 +2937,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) qe_muram_alloc(sizeof (struct ucc_geth_rx_firmware_statistics_pram), UCC_GETH_RX_STATISTICS_ALIGNMENT); - if (IS_MURAM_ERR(ugeth->rx_fw_statistics_pram_offset)) { + if (IS_ERR_VALUE(ugeth->rx_fw_statistics_pram_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for" " p_rx_fw_statistics_pram.", __FUNCTION__); @@ -2959,7 +2959,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) qe_muram_alloc(ug_info->numQueuesRx * sizeof(struct ucc_geth_rx_interrupt_coalescing_entry) + 4, UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT); - if (IS_MURAM_ERR(ugeth->rx_irq_coalescing_tbl_offset)) { + if (IS_ERR_VALUE(ugeth->rx_irq_coalescing_tbl_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for" " p_rx_irq_coalescing_tbl.", __FUNCTION__); @@ -3027,7 +3027,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) (sizeof(struct ucc_geth_rx_bd_queues_entry) + sizeof(struct ucc_geth_rx_prefetched_bds)), UCC_GETH_RX_BD_QUEUES_ALIGNMENT); - if (IS_MURAM_ERR(ugeth->rx_bd_qs_tbl_offset)) { + if (IS_ERR_VALUE(ugeth->rx_bd_qs_tbl_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for p_rx_bd_qs_tbl.", __FUNCTION__); @@ -3116,7 +3116,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) ugeth->exf_glbl_param_offset = qe_muram_alloc(sizeof(struct ucc_geth_exf_global_pram), UCC_GETH_RX_EXTENDED_FILTERING_GLOBAL_PARAMETERS_ALIGNMENT); - if (IS_MURAM_ERR(ugeth->exf_glbl_param_offset)) { + if (IS_ERR_VALUE(ugeth->exf_glbl_param_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for" " p_exf_glbl_param.", __FUNCTION__); @@ -3258,7 +3258,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) /* Allocate InitEnet command parameter structure */ init_enet_pram_offset = qe_muram_alloc(sizeof(struct ucc_geth_init_pram), 4); - if (IS_MURAM_ERR(init_enet_pram_offset)) { + if (IS_ERR_VALUE(init_enet_pram_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for p_init_enet_pram.", __FUNCTION__); diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index e273347dc606..e3f5bb0fe603 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -3,6 +3,7 @@ # menu "Wireless LAN" + depends on !S390 config WLAN_PRE80211 bool "Wireless LAN (pre-802.11)" diff --git a/drivers/parport/Kconfig b/drivers/parport/Kconfig index 36c6a1bfe558..f46c69e4ed82 100644 --- a/drivers/parport/Kconfig +++ b/drivers/parport/Kconfig @@ -6,6 +6,7 @@ # menu "Parallel port support" + depends on HAS_IOMEM config PARPORT tristate "Parallel port support" diff --git a/drivers/pnp/Kconfig b/drivers/pnp/Kconfig index c5143201419a..1959cef8e9de 100644 --- a/drivers/pnp/Kconfig +++ b/drivers/pnp/Kconfig @@ -3,6 +3,7 @@ # menu "Plug and Play support" + depends on HAS_IOMEM config PNP bool "Plug and Play support" diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 76422eded36e..95ce8f49e382 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -3,6 +3,7 @@ # menu "Real Time Clock" + depends on !S390 config RTC_LIB tristate @@ -245,7 +246,7 @@ comment "Platform RTC drivers" config RTC_DRV_CMOS tristate "PC-style 'CMOS'" depends on RTC_CLASS && (X86 || ALPHA || ARM26 || ARM \ - || M32R || ATARI || POWERPC) + || M32R || ATARI || POWERPC || MIPS) help Say "yes" here to get direct support for the real time clock found in every PC or ACPI-based system, and some other boards. @@ -396,7 +397,7 @@ config RTC_DRV_BFIN config RTC_DRV_RS5C313 tristate "Ricoh RS5C313" - depends on RTC_CLASS && BROKEN + depends on RTC_CLASS && SH_LANDISK help If you say yes here you get support for the Ricoh RS5C313 RTC chips. diff --git a/drivers/rtc/rtc-rs5c313.c b/drivers/rtc/rtc-rs5c313.c index 9d6de371495b..66eb133bf5fd 100644 --- a/drivers/rtc/rtc-rs5c313.c +++ b/drivers/rtc/rtc-rs5c313.c @@ -126,7 +126,7 @@ static void rs5c313_write_data(unsigned char data) static unsigned char rs5c313_read_data(void) { int i; - unsigned char data; + unsigned char data = 0; for (i = 0; i < 8; i++) { ndelay(700); @@ -194,7 +194,7 @@ static void rs5c313_write_reg(unsigned char addr, unsigned char data) return; } -static inline unsigned char rs5c313_read_cntreg(unsigned char addr) +static inline unsigned char rs5c313_read_cntreg(void) { return rs5c313_read_reg(RS5C313_ADDR_CNTREG); } @@ -212,7 +212,9 @@ static inline void rs5c313_write_intintvreg(unsigned char data) static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) { int data; + int cnt; + cnt = 0; while (1) { RS5C313_CEENABLE; /* CE:H */ @@ -225,6 +227,10 @@ static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) RS5C313_CEDISABLE; ndelay(700); /* CE:L */ + if (cnt++ > 100) { + dev_err(dev, "%s: timeout error\n", __FUNCTION__); + return -EIO; + } } data = rs5c313_read_reg(RS5C313_ADDR_SEC); @@ -266,7 +272,9 @@ static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) { int data; + int cnt; + cnt = 0; /* busy check. */ while (1) { RS5C313_CEENABLE; /* CE:H */ @@ -279,6 +287,11 @@ static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) RS5C313_MISCOP; RS5C313_CEDISABLE; ndelay(700); /* CE:L */ + + if (cnt++ > 100) { + dev_err(dev, "%s: timeout error\n", __FUNCTION__); + return -EIO; + } } data = BIN2BCD(tm->tm_sec); @@ -317,6 +330,7 @@ static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) static void rs5c313_check_xstp_bit(void) { struct rtc_time tm; + int cnt; RS5C313_CEENABLE; /* CE:H */ if (rs5c313_read_cntreg() & RS5C313_CNTREG_WTEN_XSTP) { @@ -326,12 +340,16 @@ static void rs5c313_check_xstp_bit(void) rs5c313_write_cntreg(0x07); /* busy check. */ - while (rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY) + for (cnt = 0; cnt < 100; cnt++) { + if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) + break; RS5C313_MISCOP; + } memset(&tm, 0, sizeof(struct rtc_time)); tm.tm_mday = 1; - tm.tm_mon = 1; + tm.tm_mon = 1 - 1; + tm.tm_year = 2000 - 1900; rs5c313_rtc_set_time(NULL, &tm); printk(KERN_ERR "RICHO RS5C313: invalid value, resetting to " @@ -356,7 +374,7 @@ static int rs5c313_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rtc); - return err; + return 0; } static int __devexit rs5c313_rtc_remove(struct platform_device *pdev) diff --git a/drivers/s390/block/Kconfig b/drivers/s390/block/Kconfig index b250c5354503..e879b212cf43 100644 --- a/drivers/s390/block/Kconfig +++ b/drivers/s390/block/Kconfig @@ -1,11 +1,9 @@ -if S390 && BLOCK - comment "S/390 block device drivers" - depends on S390 + depends on S390 && BLOCK config BLK_DEV_XPRAM tristate "XPRAM disk support" - depends on S390 + depends on S390 && BLOCK help Select this option if you want to use your expanded storage on S/390 or zSeries as a disk. This is useful as a _fast_ swap device if you @@ -15,12 +13,13 @@ config BLK_DEV_XPRAM config DCSSBLK tristate "DCSSBLK support" + depends on S390 && BLOCK help Support for dcss block device config DASD tristate "Support for DASD devices" - depends on CCW + depends on CCW && BLOCK help Enable this option if you want to access DASDs directly utilizing S/390s channel subsystem commands. This is necessary for running @@ -62,5 +61,3 @@ config DASD_EER This driver provides a character device interface to the DASD extended error reporting. This is only needed if you want to use applications written for the EER facility. - -endif diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 977521013fe8..bfeca57098fa 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -2174,9 +2174,10 @@ dasd_generic_notify(struct ccw_device *cdev, int event) return ret; } -struct dasd_ccw_req * dasd_generic_build_rdc(struct dasd_device *device, - void *rdc_buffer, - int rdc_buffer_size, char *magic) +static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, + void *rdc_buffer, + int rdc_buffer_size, + char *magic) { struct dasd_ccw_req *cqr; struct ccw1 *ccw; @@ -2219,6 +2220,7 @@ int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic, dasd_sfree_request(cqr, cqr->device); return ret; } +EXPORT_SYMBOL_GPL(dasd_generic_read_dev_chars); static int __init dasd_init(void) diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index e810e4a44ed4..eccac1c3b71b 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -50,6 +50,7 @@ struct dasd_diag_private { struct dasd_diag_rw_io iob; struct dasd_diag_init_io iib; blocknum_t pt_block; + struct ccw_dev_id dev_id; }; struct dasd_diag_req { @@ -102,7 +103,7 @@ mdsk_init_io(struct dasd_device *device, unsigned int blocksize, iib = &private->iib; memset(iib, 0, sizeof (struct dasd_diag_init_io)); - iib->dev_nr = _ccw_device_get_device_number(device->cdev); + iib->dev_nr = private->dev_id.devno; iib->block_size = blocksize; iib->offset = offset; iib->flaga = DASD_DIAG_FLAGA_DEFAULT; @@ -127,7 +128,7 @@ mdsk_term_io(struct dasd_device * device) private = (struct dasd_diag_private *) device->private; iib = &private->iib; memset(iib, 0, sizeof (struct dasd_diag_init_io)); - iib->dev_nr = _ccw_device_get_device_number(device->cdev); + iib->dev_nr = private->dev_id.devno; rc = dia250(iib, TERM_BIO); return rc; } @@ -166,7 +167,7 @@ dasd_start_diag(struct dasd_ccw_req * cqr) private = (struct dasd_diag_private *) device->private; dreq = (struct dasd_diag_req *) cqr->data; - private->iob.dev_nr = _ccw_device_get_device_number(device->cdev); + private->iob.dev_nr = private->dev_id.devno; private->iob.key = 0; private->iob.flags = DASD_DIAG_RWFLAG_ASYNC; private->iob.block_count = dreq->block_count; @@ -323,11 +324,12 @@ dasd_diag_check_device(struct dasd_device *device) "memory allocation failed for private data"); return -ENOMEM; } + ccw_device_get_id(device->cdev, &private->dev_id); device->private = (void *) private; } /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); - rdc_data->dev_nr = _ccw_device_get_device_number(device->cdev); + rdc_data->dev_nr = private->dev_id.devno; rdc_data->rdc_len = sizeof (struct dasd_diag_characteristics); rc = diag210((struct diag210 *) rdc_data); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index c9583fbc2a7d..418b4e63a4fa 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -450,9 +450,9 @@ dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid) return 0; } -struct dasd_ccw_req * dasd_eckd_build_rcd_lpm(struct dasd_device *device, - void *rcd_buffer, - struct ciw *ciw, __u8 lpm) +static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, + void *rcd_buffer, + struct ciw *ciw, __u8 lpm) { struct dasd_ccw_req *cqr; struct ccw1 *ccw; diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 758cfb542865..672eb0a3dd0b 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -255,6 +255,7 @@ dasd_ioctl_information(struct dasd_device *device, unsigned long flags; int rc; struct ccw_device *cdev; + struct ccw_dev_id dev_id; if (!device->discipline->fill_info) return -EINVAL; @@ -270,8 +271,9 @@ dasd_ioctl_information(struct dasd_device *device, } cdev = device->cdev; + ccw_device_get_id(cdev, &dev_id); - dasd_info->devno = _ccw_device_get_device_number(device->cdev); + dasd_info->devno = dev_id.devno; dasd_info->schid = _ccw_device_get_subchannel_number(device->cdev); dasd_info->cu_type = cdev->id.cu_type; dasd_info->cu_model = cdev->id.cu_model; diff --git a/drivers/s390/Kconfig b/drivers/s390/char/Kconfig index 165af398fdea..66102a184322 100644 --- a/drivers/s390/Kconfig +++ b/drivers/s390/char/Kconfig @@ -1,69 +1,9 @@ -config CCW - bool - default y - -source "drivers/block/Kconfig" - -source "drivers/md/Kconfig" - - -menu "Character device drivers" - -config UNIX98_PTYS - bool "Unix98 PTY support" - ---help--- - A pseudo terminal (PTY) is a software device consisting of two - halves: a master and a slave. The slave device behaves identical to - a physical terminal; the master device is used by a process to - read data from and write data to the slave, thereby emulating a - terminal. Typical programs for the master side are telnet servers - and xterms. - - Linux has traditionally used the BSD-like names /dev/ptyxx for - masters and /dev/ttyxx for slaves of pseudo terminals. This scheme - has a number of problems. The GNU C library glibc 2.1 and later, - however, supports the Unix98 naming standard: in order to acquire a - pseudo terminal, a process opens /dev/ptmx; the number of the pseudo - terminal is then made available to the process and the pseudo - terminal slave can be accessed as /dev/pts/<number>. What was - traditionally /dev/ttyp2 will then be /dev/pts/2, for example. - - The entries in /dev/pts/ are created on the fly by a virtual - file system; therefore, if you say Y here you should say Y to - "/dev/pts file system for Unix98 PTYs" as well. - - If you want to say Y here, you need to have the C library glibc 2.1 - or later (equal to libc-6.1, check with "ls -l /lib/libc.so.*"). - Read the instructions in <file:Documentation/Changes> pertaining to - pseudo terminals. It's safe to say N. - -config UNIX98_PTY_COUNT - int "Maximum number of Unix98 PTYs in use (0-2048)" - depends on UNIX98_PTYS - default "256" - help - The maximum number of Unix98 PTYs that can be used at any one time. - The default is 256, and should be enough for desktop systems. Server - machines which support incoming telnet/rlogin/ssh connections and/or - serve several X terminals may want to increase this: every incoming - connection and every xterm uses up one PTY. - - When not in use, each additional set of 256 PTYs occupy - approximately 8 KB of kernel memory on 32-bit architectures. - -config HANGCHECK_TIMER - tristate "Hangcheck timer" - help - The hangcheck-timer module detects when the system has gone - out to lunch past a certain margin. It can reboot the system - or merely print a warning. - -source "drivers/char/watchdog/Kconfig" - comment "S/390 character device drivers" + depends on S390 config TN3270 tristate "Support for locally attached 3270 terminals" + depends on CCW help Include support for IBM 3270 terminals. @@ -88,6 +28,7 @@ config TN3270_CONSOLE config TN3215 bool "Support for 3215 line mode terminal" + depends on CCW help Include support for IBM 3215 line-mode terminals. @@ -99,12 +40,19 @@ config TN3215_CONSOLE Linux system console. config CCW_CONSOLE - bool - depends on TN3215_CONSOLE || TN3270_CONSOLE - default y - + bool + depends on TN3215_CONSOLE || TN3270_CONSOLE + default y + +config SCLP + bool "Support for SCLP" + depends on S390 + help + Include support for the SCLP interface to the service element. + config SCLP_TTY bool "Support for SCLP line mode terminal" + depends on SCLP help Include support for IBM SCLP line-mode terminals. @@ -117,6 +65,7 @@ config SCLP_CONSOLE config SCLP_VT220_TTY bool "Support for SCLP VT220-compatible terminal" + depends on SCLP help Include support for an IBM SCLP VT220-compatible terminal. @@ -129,6 +78,7 @@ config SCLP_VT220_CONSOLE config SCLP_CPI tristate "Control-Program Identification" + depends on SCLP help This option enables the hardware console interface for system identification. This is commonly used for workload management and @@ -140,6 +90,7 @@ config SCLP_CPI config S390_TAPE tristate "S/390 tape device support" + depends on CCW help Select this option if you want to access channel-attached tape devices on IBM S/390 or zSeries. @@ -194,6 +145,7 @@ config VMLOGRDR config VMCP tristate "Support for the z/VM CP interface (VM only)" + depends on S390 help Select this option if you want to be able to interact with the control program on z/VM @@ -207,33 +159,8 @@ config MONREADER config MONWRITER tristate "API for writing z/VM monitor service records" + depends on S390 default "m" help Character device driver for writing z/VM monitor service records -endmenu - -menu "Cryptographic devices" - -config ZCRYPT - tristate "Support for PCI-attached cryptographic adapters" - select ZCRYPT_MONOLITHIC if ZCRYPT="y" - default "m" - help - Select this option if you want to use a PCI-attached cryptographic - adapter like: - + PCI Cryptographic Accelerator (PCICA) - + PCI Cryptographic Coprocessor (PCICC) - + PCI-X Cryptographic Coprocessor (PCIXCC) - + Crypto Express2 Coprocessor (CEX2C) - + Crypto Express2 Accelerator (CEX2A) - -config ZCRYPT_MONOLITHIC - bool "Monolithic zcrypt module" - depends on ZCRYPT="m" - help - Select this option if you want to have a single module z90crypt.ko - that contains all parts of the crypto device driver (ap bus, - request router and all the card drivers). - -endmenu diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 8df7b1323c05..67009bfa093e 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -97,7 +97,7 @@ static u8 user_data_sever[16] = { * Create the 8 bytes EBCDIC DCSS segment name from * an ASCII name, incl. padding */ -static inline void dcss_mkname(char *ascii_name, char *ebcdic_name) +static void dcss_mkname(char *ascii_name, char *ebcdic_name) { int i; @@ -191,7 +191,7 @@ static inline u32 mon_rec_end(struct mon_msg *monmsg) return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 8)); } -static inline int mon_check_mca(struct mon_msg *monmsg) +static int mon_check_mca(struct mon_msg *monmsg) { if ((mon_rec_end(monmsg) <= mon_rec_start(monmsg)) || (mon_rec_start(monmsg) < mon_dcss_start) || @@ -209,8 +209,8 @@ static inline int mon_check_mca(struct mon_msg *monmsg) return 0; } -static inline int mon_send_reply(struct mon_msg *monmsg, - struct mon_private *monpriv) +static int mon_send_reply(struct mon_msg *monmsg, + struct mon_private *monpriv) { int rc; @@ -236,7 +236,7 @@ static inline int mon_send_reply(struct mon_msg *monmsg, return 0; } -static inline void mon_free_mem(struct mon_private *monpriv) +static void mon_free_mem(struct mon_private *monpriv) { int i; @@ -246,7 +246,7 @@ static inline void mon_free_mem(struct mon_private *monpriv) kfree(monpriv); } -static inline struct mon_private *mon_alloc_mem(void) +static struct mon_private *mon_alloc_mem(void) { int i; struct mon_private *monpriv; @@ -307,7 +307,7 @@ static inline void mon_next_mca(struct mon_msg *monmsg) monmsg->pos = 0; } -static inline struct mon_msg *mon_next_message(struct mon_private *monpriv) +static struct mon_msg *mon_next_message(struct mon_private *monpriv) { struct mon_msg *monmsg; diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 8facd14adb7c..f6ef90ee3e7d 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -589,9 +589,10 @@ static int __raw3270_size_device_vm(struct raw3270 *rp) { int rc, model; + struct ccw_dev_id dev_id; - raw3270_init_diag210.vrdcdvno = - _ccw_device_get_device_number(rp->cdev); + ccw_device_get_id(rp->cdev, &dev_id); + raw3270_init_diag210.vrdcdvno = dev_id.devno; raw3270_init_diag210.vrdclen = sizeof(struct diag210); rc = diag210(&raw3270_init_diag210); if (rc) diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 87ac4a3ad49d..dbb99d1b6f57 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -132,6 +132,9 @@ int sclp_deactivate(void); int sclp_reactivate(void); int sclp_service_call(sclp_cmdw_t command, void *sccb); +int sclp_sdias_init(void); +void sclp_sdias_exit(void); + /* useful inlines */ /* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */ diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c index 52283daddaef..1c064976b32b 100644 --- a/drivers/s390/char/sclp_sdias.c +++ b/drivers/s390/char/sclp_sdias.c @@ -66,9 +66,9 @@ static DEFINE_MUTEX(sdias_mutex); static void sdias_callback(struct sclp_req *request, void *data) { - struct sdias_sccb *sccb; + struct sdias_sccb *cbsccb; - sccb = (struct sdias_sccb *) request->sccb; + cbsccb = (struct sdias_sccb *) request->sccb; sclp_req_done = 1; wake_up(&sdias_wq); /* Inform caller, that request is complete */ TRACE("callback done\n"); @@ -229,7 +229,7 @@ out: return rc; } -int __init sdias_init(void) +int __init sclp_sdias_init(void) { int rc; @@ -248,7 +248,7 @@ int __init sdias_init(void) return 0; } -void __exit sdias_exit(void) +void __exit sclp_sdias_exit(void) { debug_unregister(sdias_dbf); sclp_unregister(&sclp_sdias_register); diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 89d439316a53..66eb0688d523 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -21,6 +21,7 @@ #include <asm/debug.h> #include <asm/processor.h> #include <asm/irqflags.h> +#include "sclp.h" #define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) #define MSG(x...) printk( KERN_ALERT x ) @@ -564,8 +565,6 @@ static void __init zcore_header_init(int arch, struct zcore_header *hdr) get_cpu_id(&hdr->cpu_id); } -extern int sdias_init(void); - static int __init zcore_init(void) { unsigned char arch; @@ -582,7 +581,7 @@ static int __init zcore_init(void) TRACE("wwpn: %llx\n", (unsigned long long) ipl_info.data.fcp.wwpn); TRACE("lun: %llx\n", (unsigned long long) ipl_info.data.fcp.lun); - rc = sdias_init(); + rc = sclp_sdias_init(); if (rc) goto fail; @@ -634,12 +633,10 @@ fail: return rc; } -extern void sdias_exit(void); - static void __exit zcore_exit(void) { debug_unregister(zcore_dbf); - sdias_exit(); + sclp_sdias_exit(); diag308(DIAG308_REL_HSA, NULL); } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 27c6d9e55b23..dfca0ef139fd 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -191,8 +191,7 @@ static int css_register_subchannel(struct subchannel *sch) return ret; } -int -css_probe_device(struct subchannel_id schid) +static int css_probe_device(struct subchannel_id schid) { int ret; struct subchannel *sch; diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 71fcfdc42800..ed7977531c3f 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -138,9 +138,7 @@ struct css_driver { * all css_drivers have the css_bus_type */ extern struct bus_type css_bus_type; -extern struct css_driver io_subchannel_driver; -extern int css_probe_device(struct subchannel_id); extern int css_sch_device_register(struct subchannel *); extern void css_sch_device_unregister(struct subchannel *); extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index a23ff582db9d..a8b373f69cf0 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -129,7 +129,7 @@ static void io_subchannel_verify(struct device *); static void io_subchannel_ioterm(struct device *); static void io_subchannel_shutdown(struct subchannel *); -struct css_driver io_subchannel_driver = { +static struct css_driver io_subchannel_driver = { .subchannel_type = SUBCHANNEL_TYPE_IO, .drv = { .name = "io_subchannel", @@ -546,7 +546,7 @@ static struct attribute_group ccwdev_attr_group = { .attrs = ccwdev_attrs, }; -struct attribute_group *ccwdev_attr_groups[] = { +static struct attribute_group *ccwdev_attr_groups[] = { &ccwdev_attr_group, NULL, }; diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 16f59fcb66b1..a5d263fb55ae 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -616,6 +616,17 @@ ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) return chp_get_chp_desc(chpid); } +/** + * ccw_device_get_id - obtain a ccw device id + * @cdev: device to obtain the id for + * @dev_id: where to fill in the values + */ +void ccw_device_get_id(struct ccw_device *cdev, struct ccw_dev_id *dev_id) +{ + *dev_id = cdev->private->dev_id; +} +EXPORT_SYMBOL(ccw_device_get_id); + // FIXME: these have to go: int diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index f770018fe1d5..e70aeb7a3781 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -1983,6 +1983,7 @@ qdio_handle_pci(struct qdio_irq *irq_ptr) if (q->is_input_q&QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT) qdio_mark_q(q); else { + qdio_perf_stat_dec(&perf_stats.tl_runs); __qdio_inbound_processing(q); } } diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig index f98fa465df0a..eada69dec4fe 100644 --- a/drivers/s390/net/Kconfig +++ b/drivers/s390/net/Kconfig @@ -3,7 +3,7 @@ menu "S/390 network device drivers" config LCS tristate "Lan Channel Station Interface" - depends on NETDEVICES && (NET_ETHERNET || TR || FDDI) + depends on CCW && NETDEVICES && (NET_ETHERNET || TR || FDDI) help Select this option if you want to use LCS networking on IBM S/390 or zSeries. This device driver supports Token Ring (IEEE 802.5), @@ -13,7 +13,7 @@ config LCS config CTC tristate "CTC device support" - depends on NETDEVICES + depends on CCW && NETDEVICES help Select this option if you want to use channel-to-channel networking on IBM S/390 or zSeries. This device driver supports real CTC @@ -42,7 +42,7 @@ config SMSGIUCV config CLAW tristate "CLAW device support" - depends on NETDEVICES + depends on CCW && NETDEVICES help This driver supports channel attached CLAW devices. CLAW is Common Link Access for Workstation. Common devices @@ -52,7 +52,7 @@ config CLAW config QETH tristate "Gigabit Ethernet device support" - depends on NETDEVICES && IP_MULTICAST && QDIO + depends on CCW && NETDEVICES && IP_MULTICAST && QDIO help This driver supports the IBM S/390 and zSeries OSA Express adapters in QDIO mode (all media types), HiperSockets interfaces and VM GuestLAN diff --git a/drivers/s390/net/qeth_mpc.c b/drivers/s390/net/qeth_mpc.c index f54fdfdbf06f..f29a4bc4f6f2 100644 --- a/drivers/s390/net/qeth_mpc.c +++ b/drivers/s390/net/qeth_mpc.c @@ -162,7 +162,7 @@ struct ipa_rc_msg { char *msg; }; -struct ipa_rc_msg qeth_ipa_rc_msg[] = { +static struct ipa_rc_msg qeth_ipa_rc_msg[] = { {IPA_RC_SUCCESS, "success"}, {IPA_RC_NOTSUPP, "Command not supported"}, {IPA_RC_IP_TABLE_FULL, "Add Addr IP Table Full - ipv6"}, @@ -226,7 +226,7 @@ struct ipa_cmd_names { char *name; }; -struct ipa_cmd_names qeth_ipa_cmd_names[] = { +static struct ipa_cmd_names qeth_ipa_cmd_names[] = { {IPA_CMD_STARTLAN, "startlan"}, {IPA_CMD_STOPLAN, "stoplan"}, {IPA_CMD_SETVMAC, "setvmac"}, diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 324899c96efe..ddff40c4212c 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -607,8 +607,7 @@ zfcp_sg_list_free(struct zfcp_sg_list *sg_list) * @sg_count: elements in array * Return: size of entire scatter-gather list */ -size_t -zfcp_sg_size(struct scatterlist *sg, unsigned int sg_count) +static size_t zfcp_sg_size(struct scatterlist *sg, unsigned int sg_count) { unsigned int i; struct scatterlist *p; @@ -975,8 +974,7 @@ zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) mempool_destroy(adapter->pool.data_gid_pn); } -void -zfcp_dummy_release(struct device *dev) +static void zfcp_dummy_release(struct device *dev) { return; } @@ -1336,7 +1334,7 @@ zfcp_nameserver_enqueue(struct zfcp_adapter *adapter) #define ZFCP_LOG_AREA ZFCP_LOG_AREA_FC -void +static void zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter, struct fsf_status_read_buffer *status_buffer) { diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index d8191d115c14..5f3212440f68 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -478,7 +478,7 @@ static struct debug_view zfcp_hba_dbf_view = { NULL }; -void +static void _zfcp_san_dbf_event_common_ct(const char *tag, struct zfcp_fsf_req *fsf_req, u32 s_id, u32 d_id, void *buffer, int buflen) { diff --git a/drivers/scsi/sgiwd93.c b/drivers/scsi/sgiwd93.c index a15752b37990..eef82758d047 100644 --- a/drivers/scsi/sgiwd93.c +++ b/drivers/scsi/sgiwd93.c @@ -6,87 +6,49 @@ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) * Copyright (C) 2001 Florian Lohoff (flo@rfc822.org) - * Copyright (C) 2003 Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2003, 07 Ralf Baechle (ralf@linux-mips.org) * * (In all truth, Jed Schimmel wrote all this code.) */ -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/types.h> -#include <linux/mm.h> -#include <linux/blkdev.h> + +#undef DEBUG + #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/gfp.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include <linux/spinlock.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/sgialib.h> -#include <asm/sgi/sgi.h> -#include <asm/sgi/mc.h> #include <asm/sgi/hpc3.h> #include <asm/sgi/ip22.h> -#include <asm/irq.h> -#include <asm/io.h> +#include <asm/sgi/wd.h> #include "scsi.h" -#include <scsi/scsi_host.h> #include "wd33c93.h" -#include <linux/stat.h> - -#if 0 -#define DPRINTK(args...) printk(args) -#else -#define DPRINTK(args...) -#endif - -#define HDATA(ptr) ((struct ip22_hostdata *)((ptr)->hostdata)) - struct ip22_hostdata { struct WD33C93_hostdata wh; struct hpc_data { dma_addr_t dma; - void * cpu; + void *cpu; } hd; }; +#define host_to_hostdata(host) ((struct ip22_hostdata *)((host)->hostdata)) + struct hpc_chunk { struct hpc_dma_desc desc; u32 _padding; /* align to quadword boundary */ }; -struct Scsi_Host *sgiwd93_host; -struct Scsi_Host *sgiwd93_host1; - -/* Wuff wuff, wuff, wd33c93.c, wuff wuff, object oriented, bow wow. */ -static inline void write_wd33c93_count(const wd33c93_regs regs, - unsigned long value) -{ - *regs.SASR = WD_TRANSFER_COUNT_MSB; - mb(); - *regs.SCMD = ((value >> 16) & 0xff); - *regs.SCMD = ((value >> 8) & 0xff); - *regs.SCMD = ((value >> 0) & 0xff); - mb(); -} - -static inline unsigned long read_wd33c93_count(const wd33c93_regs regs) -{ - unsigned long value; - - *regs.SASR = WD_TRANSFER_COUNT_MSB; - mb(); - value = ((*regs.SCMD & 0xff) << 16); - value |= ((*regs.SCMD & 0xff) << 8); - value |= ((*regs.SCMD & 0xff) << 0); - mb(); - return value; -} - static irqreturn_t sgiwd93_intr(int irq, void *dev_id) { - struct Scsi_Host * host = (struct Scsi_Host *) dev_id; + struct Scsi_Host * host = dev_id; unsigned long flags; spin_lock_irqsave(host->host_lock, flags); @@ -131,12 +93,12 @@ void fill_hpc_entries(struct hpc_chunk *hcp, struct scsi_cmnd *cmd, int datainp) static int dma_setup(struct scsi_cmnd *cmd, int datainp) { - struct ip22_hostdata *hdata = HDATA(cmd->device->host); + struct ip22_hostdata *hdata = host_to_hostdata(cmd->device->host); struct hpc3_scsiregs *hregs = (struct hpc3_scsiregs *) cmd->device->host->base; struct hpc_chunk *hcp = (struct hpc_chunk *) hdata->hd.cpu; - DPRINTK("dma_setup: datainp<%d> hcp<%p> ", datainp, hcp); + pr_debug("dma_setup: datainp<%d> hcp<%p> ", datainp, hcp); hdata->wh.dma_dir = datainp; @@ -151,7 +113,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int datainp) fill_hpc_entries(hcp, cmd, datainp); - DPRINTK(" HPCGO\n"); + pr_debug(" HPCGO\n"); /* Start up the HPC. */ hregs->ndptr = hdata->hd.dma; @@ -166,7 +128,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int datainp) static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, int status) { - struct ip22_hostdata *hdata = HDATA(instance); + struct ip22_hostdata *hdata = host_to_hostdata(instance); struct hpc3_scsiregs *hregs; if (!SCpnt) @@ -174,7 +136,7 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, hregs = (struct hpc3_scsiregs *) SCpnt->device->host->base; - DPRINTK("dma_stop: status<%d> ", status); + pr_debug("dma_stop: status<%d> ", status); /* First stop the HPC and flush it's FIFO. */ if (hdata->wh.dma_dir) { @@ -186,7 +148,7 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, dma_unmap_single(NULL, SCpnt->SCp.dma_handle, SCpnt->SCp.this_residual, SCpnt->sc_data_direction); - DPRINTK("\n"); + pr_debug("\n"); } void sgiwd93_reset(unsigned long base) @@ -216,29 +178,71 @@ static inline void init_hpc_chain(struct hpc_data *hd) hcp->desc.pnext = hd->dma; } -static struct Scsi_Host * __init sgiwd93_setup_scsi( - struct scsi_host_template *SGIblows, int unit, int irq, - struct hpc3_scsiregs *hregs, unsigned char *wdregs) +static int sgiwd93_bus_reset(struct scsi_cmnd *cmd) +{ + /* FIXME perform bus-specific reset */ + + /* FIXME 2: kill this function, and let midlayer fallback + to the same result, calling wd33c93_host_reset() */ + + spin_lock_irq(cmd->device->host->host_lock); + wd33c93_host_reset(cmd); + spin_unlock_irq(cmd->device->host->host_lock); + + return SUCCESS; +} + +/* + * Kludge alert - the SCSI code calls the abort and reset method with int + * arguments not with pointers. So this is going to blow up beautyfully + * on 64-bit systems with memory outside the compat address spaces. + */ +static struct scsi_host_template sgiwd93_template = { + .module = THIS_MODULE, + .proc_name = "SGIWD93", + .name = "SGI WD93", + .queuecommand = wd33c93_queuecommand, + .eh_abort_handler = wd33c93_abort, + .eh_bus_reset_handler = sgiwd93_bus_reset, + .eh_host_reset_handler = wd33c93_host_reset, + .can_queue = 16, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 8, + .use_clustering = DISABLE_CLUSTERING, +}; + +static int __init sgiwd93_probe(struct platform_device *pdev) { + struct sgiwd93_platform_data *pd = pdev->dev.platform_data; + unsigned char *wdregs = pd->wdregs; + struct hpc3_scsiregs *hregs = pd->hregs; struct ip22_hostdata *hdata; struct Scsi_Host *host; wd33c93_regs regs; - - host = scsi_register(SGIblows, sizeof(struct ip22_hostdata)); - if (!host) - return NULL; + unsigned int unit = pd->unit; + unsigned int irq = pd->irq; + int err; + + host = scsi_host_alloc(&sgiwd93_template, sizeof(struct ip22_hostdata)); + if (!host) { + err = -ENOMEM; + goto out; + } host->base = (unsigned long) hregs; host->irq = irq; - hdata = HDATA(host); - hdata->hd.cpu = dma_alloc_coherent(NULL, PAGE_SIZE, &hdata->hd.dma, - GFP_KERNEL); + hdata = host_to_hostdata(host); + hdata->hd.cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, + &hdata->hd.dma, GFP_KERNEL); if (!hdata->hd.cpu) { printk(KERN_WARNING "sgiwd93: Could not allocate memory for " "host %d buffer.\n", unit); - goto out_unregister; + err = -ENOMEM; + goto out_put; } + init_hpc_chain(&hdata->hd); regs.SASR = wdregs + 3; @@ -249,95 +253,67 @@ static struct Scsi_Host * __init sgiwd93_setup_scsi( if (hdata->wh.no_sync == 0xff) hdata->wh.no_sync = 0; - if (request_irq(irq, sgiwd93_intr, 0, "SGI WD93", (void *) host)) { + err = request_irq(irq, sgiwd93_intr, 0, "SGI WD93", host); + if (err) { printk(KERN_WARNING "sgiwd93: Could not register irq %d " "for host %d.\n", irq, unit); goto out_free; } - return host; -out_free: - dma_free_coherent(NULL, PAGE_SIZE, hdata->hd.cpu, hdata->hd.dma); - wd33c93_release(); + platform_set_drvdata(pdev, host); -out_unregister: - scsi_unregister(host); + err = scsi_add_host(host, NULL); + if (err) + goto out_irq; - return NULL; -} - -static int __init sgiwd93_detect(struct scsi_host_template *SGIblows) -{ - int found = 0; - - SGIblows->proc_name = "SGIWD93"; - sgiwd93_host = sgiwd93_setup_scsi(SGIblows, 0, SGI_WD93_0_IRQ, - &hpc3c0->scsi_chan0, - (unsigned char *)hpc3c0->scsi0_ext); - if (sgiwd93_host) - found++; - - /* Set up second controller on the Indigo2 */ - if (ip22_is_fullhouse()) { - sgiwd93_host1 = sgiwd93_setup_scsi(SGIblows, 1, SGI_WD93_1_IRQ, - &hpc3c0->scsi_chan1, - (unsigned char *)hpc3c0->scsi1_ext); - if (sgiwd93_host1) - found++; - } - - return found; -} + scsi_scan_host(host); -static int sgiwd93_release(struct Scsi_Host *instance) -{ - struct ip22_hostdata *hdata = HDATA(instance); - int irq = 0; - - if (sgiwd93_host && sgiwd93_host == instance) - irq = SGI_WD93_0_IRQ; - else if (sgiwd93_host1 && sgiwd93_host1 == instance) - irq = SGI_WD93_1_IRQ; + return 0; - free_irq(irq, sgiwd93_intr); +out_irq: + free_irq(irq, host); +out_free: dma_free_coherent(NULL, PAGE_SIZE, hdata->hd.cpu, hdata->hd.dma); - wd33c93_release(); +out_put: + scsi_host_put(host); +out: - return 1; + return err; } -static int sgiwd93_bus_reset(struct scsi_cmnd *cmd) +static void __exit sgiwd93_remove(struct platform_device *pdev) { - /* FIXME perform bus-specific reset */ + struct Scsi_Host *host = platform_get_drvdata(pdev); + struct ip22_hostdata *hdata = (struct ip22_hostdata *) host->hostdata; + struct sgiwd93_platform_data *pd = pdev->dev.platform_data; + + scsi_remove_host(host); + free_irq(pd->irq, host); + dma_free_coherent(&pdev->dev, PAGE_SIZE, hdata->hd.cpu, hdata->hd.dma); + scsi_host_put(host); +} - /* FIXME 2: kill this function, and let midlayer fallback - to the same result, calling wd33c93_host_reset() */ +static struct platform_driver sgiwd93_driver = { + .probe = sgiwd93_probe, + .remove = __devexit_p(sgiwd93_remove), + .driver = { + .name = "sgiwd93" + } +}; - spin_lock_irq(cmd->device->host->host_lock); - wd33c93_host_reset(cmd); - spin_unlock_irq(cmd->device->host->host_lock); +static int __init sgiwd93_module_init(void) +{ + return platform_driver_register(&sgiwd93_driver); +} - return SUCCESS; +static void __exit sgiwd93_module_exit(void) +{ + return platform_driver_unregister(&sgiwd93_driver); } -/* - * Kludge alert - the SCSI code calls the abort and reset method with int - * arguments not with pointers. So this is going to blow up beautyfully - * on 64-bit systems with memory outside the compat address spaces. - */ -static struct scsi_host_template driver_template = { - .proc_name = "SGIWD93", - .name = "SGI WD93", - .detect = sgiwd93_detect, - .release = sgiwd93_release, - .queuecommand = wd33c93_queuecommand, - .eh_abort_handler = wd33c93_abort, - .eh_bus_reset_handler = sgiwd93_bus_reset, - .eh_host_reset_handler = wd33c93_host_reset, - .can_queue = 16, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 8, - .use_clustering = DISABLE_CLUSTERING, -}; -#include "scsi_module.c" +module_init(sgiwd93_module_init); +module_exit(sgiwd93_module_exit); + +MODULE_DESCRIPTION("SGI WD33C93 driver"); +MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index e8efe938c4e7..a6f5bfbb777b 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -5,6 +5,7 @@ # menu "Serial drivers" + depends on HAS_IOMEM # # The new 8250/16550 serial drivers diff --git a/drivers/serial/cpm_uart/cpm_uart.h b/drivers/serial/cpm_uart/cpm_uart.h index 69715e556506..a8f894c78194 100644 --- a/drivers/serial/cpm_uart/cpm_uart.h +++ b/drivers/serial/cpm_uart/cpm_uart.h @@ -88,7 +88,7 @@ extern struct uart_cpm_port cpm_uart_ports[UART_NR]; /* these are located in their respective files */ void cpm_line_cr_cmd(int line, int cmd); -int __init cpm_uart_init_portdesc(void); +int cpm_uart_init_portdesc(void); int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con); void cpm_uart_freebuf(struct uart_cpm_port *pinfo); diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index f23972bc00c0..b63ff8dd7304 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -482,7 +482,8 @@ static void cpm_uart_shutdown(struct uart_port *port) } static void cpm_uart_set_termios(struct uart_port *port, - struct termios *termios, struct termios *old) + struct ktermios *termios, + struct ktermios *old) { int baud; unsigned long flags; diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c index 925fb607d8c4..8c6324ed0202 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.c @@ -125,7 +125,7 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) { int dpmemsz, memsz; u8 *dp_mem; - uint dp_offset; + unsigned long dp_offset; u8 *mem_addr; dma_addr_t dma_addr = 0; @@ -133,7 +133,7 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos); dp_offset = cpm_dpalloc(dpmemsz, 8); - if (IS_DPERR(dp_offset)) { + if (IS_ERR_VALUE(dp_offset)) { printk(KERN_ERR "cpm_uart_cpm1.c: could not allocate buffer descriptors\n"); return -ENOMEM; @@ -185,7 +185,7 @@ void cpm_uart_freebuf(struct uart_cpm_port *pinfo) } /* Setup any dynamic params in the uart desc */ -int __init cpm_uart_init_portdesc(void) +int cpm_uart_init_portdesc(void) { pr_debug("CPM uart[-]:init portdesc\n"); diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c index fa455996ad8f..7b61d805ebe9 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -222,7 +222,7 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) { int dpmemsz, memsz; u8 *dp_mem; - uint dp_offset; + unsigned long dp_offset; u8 *mem_addr; dma_addr_t dma_addr = 0; @@ -230,7 +230,7 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos); dp_offset = cpm_dpalloc(dpmemsz, 8); - if (IS_DPERR(dp_offset)) { + if (IS_ERR_VALUE(dp_offset)) { printk(KERN_ERR "cpm_uart_cpm.c: could not allocate buffer descriptors\n"); return -ENOMEM; @@ -282,7 +282,7 @@ void cpm_uart_freebuf(struct uart_cpm_port *pinfo) } /* Setup any dynamic params in the uart desc */ -int __init cpm_uart_init_portdesc(void) +int cpm_uart_init_portdesc(void) { #if defined(CONFIG_SERIAL_CPM_SMC1) || defined(CONFIG_SERIAL_CPM_SMC2) u16 *addr; diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index da73205e54cd..0985193dc57d 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -92,6 +92,8 @@ struct uart_sunzilog_port { #define SUNZILOG_FLAG_REGS_HELD 0x00000040 #define SUNZILOG_FLAG_TX_STOPPED 0x00000080 #define SUNZILOG_FLAG_TX_ACTIVE 0x00000100 +#define SUNZILOG_FLAG_ESCC 0x00000200 +#define SUNZILOG_FLAG_ISR_HANDLER 0x00000400 unsigned int cflag; @@ -174,9 +176,11 @@ static void sunzilog_clear_fifo(struct zilog_channel __iomem *channel) /* This function must only be called when the TX is not busy. The UART * port lock must be held and local interrupts disabled. */ -static void __load_zsregs(struct zilog_channel __iomem *channel, unsigned char *regs) +static int __load_zsregs(struct zilog_channel __iomem *channel, unsigned char *regs) { int i; + int escc; + unsigned char r15; /* Let pending transmits finish. */ for (i = 0; i < 1000; i++) { @@ -229,11 +233,25 @@ static void __load_zsregs(struct zilog_channel __iomem *channel, unsigned char * write_zsreg(channel, R14, regs[R14]); /* External status interrupt control. */ - write_zsreg(channel, R15, regs[R15]); + write_zsreg(channel, R15, (regs[R15] | WR7pEN) & ~FIFOEN); + + /* ESCC Extension Register */ + r15 = read_zsreg(channel, R15); + if (r15 & 0x01) { + write_zsreg(channel, R7, regs[R7p]); + + /* External status interrupt and FIFO control. */ + write_zsreg(channel, R15, regs[R15] & ~WR7pEN); + escc = 1; + } else { + /* Clear FIFO bit case it is an issue */ + regs[R15] &= ~FIFOEN; + escc = 0; + } /* Reset external status interrupts. */ - write_zsreg(channel, R0, RES_EXT_INT); - write_zsreg(channel, R0, RES_EXT_INT); + write_zsreg(channel, R0, RES_EXT_INT); /* First Latch */ + write_zsreg(channel, R0, RES_EXT_INT); /* Second Latch */ /* Rewrite R3/R5, this time without enables masked. */ write_zsreg(channel, R3, regs[R3]); @@ -241,6 +259,8 @@ static void __load_zsregs(struct zilog_channel __iomem *channel, unsigned char * /* Rewrite R1, this time without IRQ enabled masked. */ write_zsreg(channel, R1, regs[R1]); + + return escc; } /* Reprogram the Zilog channel HW registers with the copies found in the @@ -731,7 +751,7 @@ static void sunzilog_enable_ms(struct uart_port *port) up->curregs[R15] = new_reg; /* NOTE: Not subject to 'transmitter active' rule. */ - write_zsreg(channel, R15, up->curregs[R15]); + write_zsreg(channel, R15, up->curregs[R15] & ~WR7pEN); } } @@ -861,44 +881,44 @@ sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag, up->curregs[R14] = BRSRC | BRENAB; /* Character size, stop bits, and parity. */ - up->curregs[3] &= ~RxN_MASK; - up->curregs[5] &= ~TxN_MASK; + up->curregs[R3] &= ~RxN_MASK; + up->curregs[R5] &= ~TxN_MASK; switch (cflag & CSIZE) { case CS5: - up->curregs[3] |= Rx5; - up->curregs[5] |= Tx5; + up->curregs[R3] |= Rx5; + up->curregs[R5] |= Tx5; up->parity_mask = 0x1f; break; case CS6: - up->curregs[3] |= Rx6; - up->curregs[5] |= Tx6; + up->curregs[R3] |= Rx6; + up->curregs[R5] |= Tx6; up->parity_mask = 0x3f; break; case CS7: - up->curregs[3] |= Rx7; - up->curregs[5] |= Tx7; + up->curregs[R3] |= Rx7; + up->curregs[R5] |= Tx7; up->parity_mask = 0x7f; break; case CS8: default: - up->curregs[3] |= Rx8; - up->curregs[5] |= Tx8; + up->curregs[R3] |= Rx8; + up->curregs[R5] |= Tx8; up->parity_mask = 0xff; break; }; - up->curregs[4] &= ~0x0c; + up->curregs[R4] &= ~0x0c; if (cflag & CSTOPB) - up->curregs[4] |= SB2; + up->curregs[R4] |= SB2; else - up->curregs[4] |= SB1; + up->curregs[R4] |= SB1; if (cflag & PARENB) - up->curregs[4] |= PAR_ENAB; + up->curregs[R4] |= PAR_ENAB; else - up->curregs[4] &= ~PAR_ENAB; + up->curregs[R4] &= ~PAR_ENAB; if (!(cflag & PARODD)) - up->curregs[4] |= PAR_EVEN; + up->curregs[R4] |= PAR_EVEN; else - up->curregs[4] &= ~PAR_EVEN; + up->curregs[R4] &= ~PAR_EVEN; up->port.read_status_mask = Rx_OVR; if (iflag & INPCK) @@ -952,7 +972,9 @@ sunzilog_set_termios(struct uart_port *port, struct ktermios *termios, static const char *sunzilog_type(struct uart_port *port) { - return "zs"; + struct uart_sunzilog_port *up = UART_ZILOG(port); + + return (up->flags & SUNZILOG_FLAG_ESCC) ? "zs (ESCC)" : "zs"; } /* We do not request/release mappings of the registers here, this @@ -1170,7 +1192,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options) spin_lock_irqsave(&up->port.lock, flags); - up->curregs[R15] = BRKIE; + up->curregs[R15] |= BRKIE; sunzilog_convert_to_zs(up, con->cflag, 0, brg); sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); @@ -1229,7 +1251,7 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe baud = 4800; } - up->curregs[R15] = BRKIE; + up->curregs[R15] |= BRKIE; brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); sunzilog_convert_to_zs(up, up->cflag, 0, brg); sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); @@ -1283,8 +1305,18 @@ static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up) if (up->flags & (SUNZILOG_FLAG_CONS_KEYB | SUNZILOG_FLAG_CONS_MOUSE)) { + up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; + up->curregs[R4] = PAR_EVEN | X16CLK | SB1; + up->curregs[R3] = RxENAB | Rx8; + up->curregs[R5] = TxENAB | Tx8; + up->curregs[R6] = 0x00; /* SDLC Address */ + up->curregs[R7] = 0x7E; /* SDLC Flag */ + up->curregs[R9] = NV; + up->curregs[R7p] = 0x00; sunzilog_init_kbdms(up, up->port.line); - up->curregs[R9] |= (NV | MIE); + /* Only enable interrupts if an ISR handler available */ + if (up->flags & SUNZILOG_FLAG_ISR_HANDLER) + up->curregs[R9] |= MIE; write_zsreg(channel, R9, up->curregs[R9]); } else { /* Normal serial TTY. */ @@ -1293,7 +1325,9 @@ static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up) up->curregs[R4] = PAR_EVEN | X16CLK | SB1; up->curregs[R3] = RxENAB | Rx8; up->curregs[R5] = TxENAB | Tx8; - up->curregs[R9] = NV | MIE; + up->curregs[R6] = 0x00; /* SDLC Address */ + up->curregs[R7] = 0x7E; /* SDLC Flag */ + up->curregs[R9] = NV; up->curregs[R10] = NRZ; up->curregs[R11] = TCBR | RCBR; baud = 9600; @@ -1301,7 +1335,14 @@ static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up) up->curregs[R12] = (brg & 0xff); up->curregs[R13] = (brg >> 8) & 0xff; up->curregs[R14] = BRSRC | BRENAB; - __load_zsregs(channel, up->curregs); + up->curregs[R15] = FIFOEN; /* Use FIFO if on ESCC */ + up->curregs[R7p] = TxFIFO_LVL | RxFIFO_LVL; + if (__load_zsregs(channel, up->curregs)) { + up->flags |= SUNZILOG_FLAG_ESCC; + } + /* Only enable interrupts if an ISR handler available */ + if (up->flags & SUNZILOG_FLAG_ISR_HANDLER) + up->curregs[R9] |= MIE; write_zsreg(channel, R9, up->curregs[R9]); } @@ -1390,12 +1431,14 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m return err; } } else { - printk(KERN_INFO "%s: Keyboard at MMIO %lx (irq = %d) " - "is a zs\n", - op->dev.bus_id, up[0].port.mapbase, op->irqs[0]); - printk(KERN_INFO "%s: Mouse at MMIO %lx (irq = %d) " - "is a zs\n", - op->dev.bus_id, up[1].port.mapbase, op->irqs[0]); + printk(KERN_INFO "%s: Keyboard at MMIO 0x%lx (irq = %d) " + "is a %s\n", + op->dev.bus_id, up[0].port.mapbase, op->irqs[0], + sunzilog_type (&up[0].port)); + printk(KERN_INFO "%s: Mouse at MMIO 0x%lx (irq = %d) " + "is a %s\n", + op->dev.bus_id, up[1].port.mapbase, op->irqs[0], + sunzilog_type (&up[1].port)); } dev_set_drvdata(&op->dev, &up[0]); @@ -1487,10 +1530,23 @@ static int __init sunzilog_init(void) goto out_unregister_uart; if (zilog_irq != -1) { + struct uart_sunzilog_port *up = sunzilog_irq_chain; err = request_irq(zilog_irq, sunzilog_interrupt, IRQF_SHARED, "zs", sunzilog_irq_chain); if (err) goto out_unregister_driver; + + /* Enable Interrupts */ + while (up) { + struct zilog_channel __iomem *channel; + + /* printk (KERN_INFO "Enable IRQ for ZILOG Hardware %p\n", up); */ + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + up->flags |= SUNZILOG_FLAG_ISR_HANDLER; + up->curregs[R9] |= MIE; + write_zsreg(channel, R9, up->curregs[R9]); + up = up->next; + } } out: @@ -1515,6 +1571,20 @@ static void __exit sunzilog_exit(void) of_unregister_driver(&zs_driver); if (zilog_irq != -1) { + struct uart_sunzilog_port *up = sunzilog_irq_chain; + + /* Disable Interrupts */ + while (up) { + struct zilog_channel __iomem *channel; + + /* printk (KERN_INFO "Disable IRQ for ZILOG Hardware %p\n", up); */ + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + up->flags &= ~SUNZILOG_FLAG_ISR_HANDLER; + up->curregs[R9] &= ~MIE; + write_zsreg(channel, R9, up->curregs[R9]); + up = up->next; + } + free_irq(zilog_irq, sunzilog_irq_chain); zilog_irq = -1; } diff --git a/drivers/serial/sunzilog.h b/drivers/serial/sunzilog.h index 7939b6d71270..5dec7b47cc38 100644 --- a/drivers/serial/sunzilog.h +++ b/drivers/serial/sunzilog.h @@ -13,7 +13,8 @@ struct zilog_layout { struct zilog_channel channelA; }; -#define NUM_ZSREGS 16 +#define NUM_ZSREGS 17 +#define R7p 16 /* Written as R7 with P15 bit 0 set */ /* Conversion routines to/from brg time constants from/to bits * per second. @@ -127,6 +128,15 @@ struct zilog_layout { /* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ +/* Write Register 7' (ESCC Only) */ +#define AUTO_TxFLAG 1 /* Automatic Tx SDLC Flag */ +#define AUTO_EOM_RST 2 /* Automatic EOM Reset */ +#define AUTOnRTS 4 /* Automatic /RTS pin deactivation */ +#define RxFIFO_LVL 8 /* Receive FIFO interrupt level */ +#define nDTRnREQ 0x10 /* /DTR/REQ timing */ +#define TxFIFO_LVL 0x20 /* Transmit FIFO interrupt level */ +#define EXT_RD_EN 0x40 /* Extended read register enable */ + /* Write Register 8 (transmit buffer) */ /* Write Register 9 (Master interrupt control) */ @@ -135,6 +145,7 @@ struct zilog_layout { #define DLC 4 /* Disable Lower Chain */ #define MIE 8 /* Master Interrupt Enable */ #define STATHI 0x10 /* Status high */ +#define SWIACK 0x20 /* Software Interrupt Ack (not on NMOS) */ #define NORESET 0 /* No reset on write to R9 */ #define CHRB 0x40 /* Reset channel B */ #define CHRA 0x80 /* Reset channel A */ @@ -187,7 +198,9 @@ struct zilog_layout { #define SNRZI 0xe0 /* Set NRZI mode */ /* Write Register 15 (external/status interrupt control) */ +#define WR7pEN 1 /* WR7' Enable (ESCC only) */ #define ZCIE 2 /* Zero count IE */ +#define FIFOEN 4 /* FIFO Enable (ESCC only) */ #define DCDIE 8 /* DCD IE */ #define SYNCIE 0x10 /* Sync/hunt IE */ #define CTSIE 0x20 /* CTS IE */ @@ -241,6 +254,10 @@ struct zilog_layout { #define CHATxIP 0x10 /* Channel A Tx IP */ #define CHARxIP 0x20 /* Channel A Rx IP */ +/* Read Register 6 (LSB frame byte count [Not on NMOS]) */ + +/* Read Register 7 (MSB frame byte count and FIFO status [Not on NMOS]) */ + /* Read Register 8 (receive data register) */ /* Read Register 10 (misc status bits) */ diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 07c587ec71be..5e3f748f2693 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -6,6 +6,7 @@ # fully appropriate there, so it'd need some thought to do well. # menu "SPI support" + depends on HAS_IOMEM config SPI bool "SPI support" @@ -106,6 +107,13 @@ config SPI_IMX This enables using the Freescale iMX SPI controller in master mode. +config SPI_MPC52xx_PSC + tristate "Freescale MPC52xx PSC SPI controller" + depends on SPI_MASTER && PPC_MPC52xx && EXPERIMENTAL + help + This enables using the Freescale MPC52xx Programmable Serial + Controller in master SPI mode. + config SPI_MPC83xx tristate "Freescale MPC83xx SPI controller" depends on SPI_MASTER && PPC_83xx && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 624b6363f490..5788d867de84 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o +obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c new file mode 100644 index 000000000000..052359fc41ee --- /dev/null +++ b/drivers/spi/mpc52xx_psc_spi.c @@ -0,0 +1,654 @@ +/* + * MPC52xx SPC in SPI mode driver. + * + * Maintainer: Dragos Carp + * + * Copyright (C) 2006 TOPTICA Photonics AG. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/interrupt.h> + +#if defined(CONFIG_PPC_MERGE) +#include <asm/of_platform.h> +#else +#include <linux/platform_device.h> +#endif + +#include <linux/workqueue.h> +#include <linux/completion.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/fsl_devices.h> + +#include <asm/mpc52xx.h> +#include <asm/mpc52xx_psc.h> + +#define MCLK 20000000 /* PSC port MClk in hz */ + +struct mpc52xx_psc_spi { + /* fsl_spi_platform data */ + void (*activate_cs)(u8, u8); + void (*deactivate_cs)(u8, u8); + u32 sysclk; + + /* driver internal data */ + struct mpc52xx_psc __iomem *psc; + unsigned int irq; + u8 bits_per_word; + u8 busy; + + struct workqueue_struct *workqueue; + struct work_struct work; + + struct list_head queue; + spinlock_t lock; + + struct completion done; +}; + +/* controller state */ +struct mpc52xx_psc_spi_cs { + int bits_per_word; + int speed_hz; +}; + +/* set clock freq, clock ramp, bits per work + * if t is NULL then reset the values to the default values + */ +static int mpc52xx_psc_spi_transfer_setup(struct spi_device *spi, + struct spi_transfer *t) +{ + struct mpc52xx_psc_spi_cs *cs = spi->controller_state; + + cs->speed_hz = (t && t->speed_hz) + ? t->speed_hz : spi->max_speed_hz; + cs->bits_per_word = (t && t->bits_per_word) + ? t->bits_per_word : spi->bits_per_word; + cs->bits_per_word = ((cs->bits_per_word + 7) / 8) * 8; + return 0; +} + +static void mpc52xx_psc_spi_activate_cs(struct spi_device *spi) +{ + struct mpc52xx_psc_spi_cs *cs = spi->controller_state; + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + struct mpc52xx_psc __iomem *psc = mps->psc; + u32 sicr; + u16 ccr; + + sicr = in_be32(&psc->sicr); + + /* Set clock phase and polarity */ + if (spi->mode & SPI_CPHA) + sicr |= 0x00001000; + else + sicr &= ~0x00001000; + if (spi->mode & SPI_CPOL) + sicr |= 0x00002000; + else + sicr &= ~0x00002000; + + if (spi->mode & SPI_LSB_FIRST) + sicr |= 0x10000000; + else + sicr &= ~0x10000000; + out_be32(&psc->sicr, sicr); + + /* Set clock frequency and bits per word + * Because psc->ccr is defined as 16bit register instead of 32bit + * just set the lower byte of BitClkDiv + */ + ccr = in_be16(&psc->ccr); + ccr &= 0xFF00; + if (cs->speed_hz) + ccr |= (MCLK / cs->speed_hz - 1) & 0xFF; + else /* by default SPI Clk 1MHz */ + ccr |= (MCLK / 1000000 - 1) & 0xFF; + out_be16(&psc->ccr, ccr); + mps->bits_per_word = cs->bits_per_word; + + if (mps->activate_cs) + mps->activate_cs(spi->chip_select, + (spi->mode & SPI_CS_HIGH) ? 1 : 0); +} + +static void mpc52xx_psc_spi_deactivate_cs(struct spi_device *spi) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + + if (mps->deactivate_cs) + mps->deactivate_cs(spi->chip_select, + (spi->mode & SPI_CS_HIGH) ? 1 : 0); +} + +#define MPC52xx_PSC_BUFSIZE (MPC52xx_PSC_RFNUM_MASK + 1) +/* wake up when 80% fifo full */ +#define MPC52xx_PSC_RFALARM (MPC52xx_PSC_BUFSIZE * 20 / 100) + +static int mpc52xx_psc_spi_transfer_rxtx(struct spi_device *spi, + struct spi_transfer *t) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + struct mpc52xx_psc __iomem *psc = mps->psc; + unsigned rb = 0; /* number of bytes receieved */ + unsigned sb = 0; /* number of bytes sent */ + unsigned char *rx_buf = (unsigned char *)t->rx_buf; + unsigned char *tx_buf = (unsigned char *)t->tx_buf; + unsigned rfalarm; + unsigned send_at_once = MPC52xx_PSC_BUFSIZE; + unsigned recv_at_once; + unsigned bpw = mps->bits_per_word / 8; + + if (!t->tx_buf && !t->rx_buf && t->len) + return -EINVAL; + + /* enable transmiter/receiver */ + out_8(&psc->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); + while (rb < t->len) { + if (t->len - rb > MPC52xx_PSC_BUFSIZE) { + rfalarm = MPC52xx_PSC_RFALARM; + } else { + send_at_once = t->len - sb; + rfalarm = MPC52xx_PSC_BUFSIZE - (t->len - rb); + } + + dev_dbg(&spi->dev, "send %d bytes...\n", send_at_once); + if (tx_buf) { + for (; send_at_once; sb++, send_at_once--) { + /* set EOF flag */ + if (mps->bits_per_word + && (sb + 1) % bpw == 0) + out_8(&psc->ircr2, 0x01); + out_8(&psc->mpc52xx_psc_buffer_8, tx_buf[sb]); + } + } else { + for (; send_at_once; sb++, send_at_once--) { + /* set EOF flag */ + if (mps->bits_per_word + && ((sb + 1) % bpw) == 0) + out_8(&psc->ircr2, 0x01); + out_8(&psc->mpc52xx_psc_buffer_8, 0); + } + } + + + /* enable interupts and wait for wake up + * if just one byte is expected the Rx FIFO genererates no + * FFULL interrupt, so activate the RxRDY interrupt + */ + out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); + if (t->len - rb == 1) { + out_8(&psc->mode, 0); + } else { + out_8(&psc->mode, MPC52xx_PSC_MODE_FFULL); + out_be16(&psc->rfalarm, rfalarm); + } + out_be16(&psc->mpc52xx_psc_imr, MPC52xx_PSC_IMR_RXRDY); + wait_for_completion(&mps->done); + recv_at_once = in_be16(&psc->rfnum); + dev_dbg(&spi->dev, "%d bytes received\n", recv_at_once); + + send_at_once = recv_at_once; + if (rx_buf) { + for (; recv_at_once; rb++, recv_at_once--) + rx_buf[rb] = in_8(&psc->mpc52xx_psc_buffer_8); + } else { + for (; recv_at_once; rb++, recv_at_once--) + in_8(&psc->mpc52xx_psc_buffer_8); + } + } + /* disable transmiter/receiver */ + out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); + + return 0; +} + +static void mpc52xx_psc_spi_work(struct work_struct *work) +{ + struct mpc52xx_psc_spi *mps = + container_of(work, struct mpc52xx_psc_spi, work); + + spin_lock_irq(&mps->lock); + mps->busy = 1; + while (!list_empty(&mps->queue)) { + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + unsigned cs_change; + int status; + + m = container_of(mps->queue.next, struct spi_message, queue); + list_del_init(&m->queue); + spin_unlock_irq(&mps->lock); + + spi = m->spi; + cs_change = 1; + status = 0; + list_for_each_entry (t, &m->transfers, transfer_list) { + if (t->bits_per_word || t->speed_hz) { + status = mpc52xx_psc_spi_transfer_setup(spi, t); + if (status < 0) + break; + } + + if (cs_change) + mpc52xx_psc_spi_activate_cs(spi); + cs_change = t->cs_change; + + status = mpc52xx_psc_spi_transfer_rxtx(spi, t); + if (status) + break; + m->actual_length += t->len; + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (cs_change) + mpc52xx_psc_spi_deactivate_cs(spi); + } + + m->status = status; + m->complete(m->context); + + if (status || !cs_change) + mpc52xx_psc_spi_deactivate_cs(spi); + + mpc52xx_psc_spi_transfer_setup(spi, NULL); + + spin_lock_irq(&mps->lock); + } + mps->busy = 0; + spin_unlock_irq(&mps->lock); +} + +static int mpc52xx_psc_spi_setup(struct spi_device *spi) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + struct mpc52xx_psc_spi_cs *cs = spi->controller_state; + unsigned long flags; + + if (spi->bits_per_word%8) + return -EINVAL; + + if (!cs) { + cs = kzalloc(sizeof *cs, GFP_KERNEL); + if (!cs) + return -ENOMEM; + spi->controller_state = cs; + } + + cs->bits_per_word = spi->bits_per_word; + cs->speed_hz = spi->max_speed_hz; + + spin_lock_irqsave(&mps->lock, flags); + if (!mps->busy) + mpc52xx_psc_spi_deactivate_cs(spi); + spin_unlock_irqrestore(&mps->lock, flags); + + return 0; +} + +static int mpc52xx_psc_spi_transfer(struct spi_device *spi, + struct spi_message *m) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + unsigned long flags; + + m->actual_length = 0; + m->status = -EINPROGRESS; + + spin_lock_irqsave(&mps->lock, flags); + list_add_tail(&m->queue, &mps->queue); + queue_work(mps->workqueue, &mps->work); + spin_unlock_irqrestore(&mps->lock, flags); + + return 0; +} + +static void mpc52xx_psc_spi_cleanup(struct spi_device *spi) +{ + kfree(spi->controller_state); +} + +static int mpc52xx_psc_spi_port_config(int psc_id, struct mpc52xx_psc_spi *mps) +{ + struct mpc52xx_cdm __iomem *cdm; + struct mpc52xx_gpio __iomem *gpio; + struct mpc52xx_psc __iomem *psc = mps->psc; + u32 ul; + u32 mclken_div; + int ret = 0; + +#if defined(CONFIG_PPC_MERGE) + cdm = mpc52xx_find_and_map("mpc52xx-cdm"); + gpio = mpc52xx_find_and_map("mpc52xx-gpio"); +#else + cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE); + gpio = ioremap(MPC52xx_PA(MPC52xx_GPIO_OFFSET), MPC52xx_GPIO_SIZE); +#endif + if (!cdm || !gpio) { + printk(KERN_ERR "Error mapping CDM/GPIO\n"); + ret = -EFAULT; + goto unmap_regs; + } + + /* default sysclk is 512MHz */ + mclken_div = 0x8000 | + (((mps->sysclk ? mps->sysclk : 512000000) / MCLK) & 0x1FF); + + switch (psc_id) { + case 1: + ul = in_be32(&gpio->port_config); + ul &= 0xFFFFFFF8; + ul |= 0x00000006; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc1, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000020; + out_be32(&cdm->clk_enables, ul); + break; + case 2: + ul = in_be32(&gpio->port_config); + ul &= 0xFFFFFF8F; + ul |= 0x00000060; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc2, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000040; + out_be32(&cdm->clk_enables, ul); + break; + case 3: + ul = in_be32(&gpio->port_config); + ul &= 0xFFFFF0FF; + ul |= 0x00000600; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc3, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000080; + out_be32(&cdm->clk_enables, ul); + break; + case 6: + ul = in_be32(&gpio->port_config); + ul &= 0xFF8FFFFF; + ul |= 0x00700000; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc6, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000010; + out_be32(&cdm->clk_enables, ul); + break; + default: + ret = -EINVAL; + goto unmap_regs; + } + + /* Reset the PSC into a known state */ + out_8(&psc->command, MPC52xx_PSC_RST_RX); + out_8(&psc->command, MPC52xx_PSC_RST_TX); + out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); + + /* Disable interrupts, interrupts are based on alarm level */ + out_be16(&psc->mpc52xx_psc_imr, 0); + out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); + out_8(&psc->rfcntl, 0); + out_8(&psc->mode, MPC52xx_PSC_MODE_FFULL); + + /* Configure 8bit codec mode as a SPI master and use EOF flags */ + /* SICR_SIM_CODEC8|SICR_GENCLK|SICR_SPI|SICR_MSTR|SICR_USEEOF */ + out_be32(&psc->sicr, 0x0180C800); + out_be16(&psc->ccr, 0x070F); /* by default SPI Clk 1MHz */ + + /* Set 2ms DTL delay */ + out_8(&psc->ctur, 0x00); + out_8(&psc->ctlr, 0x84); + + mps->bits_per_word = 8; + +unmap_regs: + if (cdm) + iounmap(cdm); + if (gpio) + iounmap(gpio); + + return ret; +} + +static irqreturn_t mpc52xx_psc_spi_isr(int irq, void *dev_id) +{ + struct mpc52xx_psc_spi *mps = (struct mpc52xx_psc_spi *)dev_id; + struct mpc52xx_psc __iomem *psc = mps->psc; + + /* disable interrupt and wake up the work queue */ + if (in_be16(&psc->mpc52xx_psc_isr) & MPC52xx_PSC_IMR_RXRDY) { + out_be16(&psc->mpc52xx_psc_imr, 0); + complete(&mps->done); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/* bus_num is used only for the case dev->platform_data == NULL */ +static int __init mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr, + u32 size, unsigned int irq, s16 bus_num) +{ + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct mpc52xx_psc_spi *mps; + struct spi_master *master; + int ret; + + if (pdata == NULL) + return -ENODEV; + + master = spi_alloc_master(dev, sizeof *mps); + if (master == NULL) + return -ENOMEM; + + dev_set_drvdata(dev, master); + mps = spi_master_get_devdata(master); + + mps->irq = irq; + if (pdata == NULL) { + dev_warn(dev, "probe called without platform data, no " + "(de)activate_cs function will be called\n"); + mps->activate_cs = NULL; + mps->deactivate_cs = NULL; + mps->sysclk = 0; + master->bus_num = bus_num; + master->num_chipselect = 255; + } else { + mps->activate_cs = pdata->activate_cs; + mps->deactivate_cs = pdata->deactivate_cs; + mps->sysclk = pdata->sysclk; + master->bus_num = pdata->bus_num; + master->num_chipselect = pdata->max_chipselect; + } + master->setup = mpc52xx_psc_spi_setup; + master->transfer = mpc52xx_psc_spi_transfer; + master->cleanup = mpc52xx_psc_spi_cleanup; + + mps->psc = ioremap(regaddr, size); + if (!mps->psc) { + dev_err(dev, "could not ioremap I/O port range\n"); + ret = -EFAULT; + goto free_master; + } + + ret = request_irq(mps->irq, mpc52xx_psc_spi_isr, 0, "mpc52xx-psc-spi", + mps); + if (ret) + goto free_master; + + ret = mpc52xx_psc_spi_port_config(master->bus_num, mps); + if (ret < 0) + goto free_irq; + + spin_lock_init(&mps->lock); + init_completion(&mps->done); + INIT_WORK(&mps->work, mpc52xx_psc_spi_work); + INIT_LIST_HEAD(&mps->queue); + + mps->workqueue = create_singlethread_workqueue( + master->cdev.dev->bus_id); + if (mps->workqueue == NULL) { + ret = -EBUSY; + goto free_irq; + } + + ret = spi_register_master(master); + if (ret < 0) + goto unreg_master; + + return ret; + +unreg_master: + destroy_workqueue(mps->workqueue); +free_irq: + free_irq(mps->irq, mps); +free_master: + if (mps->psc) + iounmap(mps->psc); + spi_master_put(master); + + return ret; +} + +static int __exit mpc52xx_psc_spi_do_remove(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); + + flush_workqueue(mps->workqueue); + destroy_workqueue(mps->workqueue); + spi_unregister_master(master); + free_irq(mps->irq, mps); + if (mps->psc) + iounmap(mps->psc); + + return 0; +} + +#if !defined(CONFIG_PPC_MERGE) +static int __init mpc52xx_psc_spi_probe(struct platform_device *dev) +{ + switch(dev->id) { + case 1: + case 2: + case 3: + case 6: + return mpc52xx_psc_spi_do_probe(&dev->dev, + MPC52xx_PA(MPC52xx_PSCx_OFFSET(dev->id)), + MPC52xx_PSC_SIZE, platform_get_irq(dev, 0), dev->id); + default: + return -EINVAL; + } +} + +static int __exit mpc52xx_psc_spi_remove(struct platform_device *dev) +{ + return mpc52xx_psc_spi_do_remove(&dev->dev); +} + +static struct platform_driver mpc52xx_psc_spi_platform_driver = { + .remove = __exit_p(mpc52xx_psc_spi_remove), + .driver = { + .name = "mpc52xx-psc-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init mpc52xx_psc_spi_init(void) +{ + return platform_driver_probe(&mpc52xx_psc_spi_platform_driver, + mpc52xx_psc_spi_probe); +} +module_init(mpc52xx_psc_spi_init); + +static void __exit mpc52xx_psc_spi_exit(void) +{ + platform_driver_unregister(&mpc52xx_psc_spi_platform_driver); +} +module_exit(mpc52xx_psc_spi_exit); + +#else /* defined(CONFIG_PPC_MERGE) */ + +static int __init mpc52xx_psc_spi_of_probe(struct of_device *op, + const struct of_device_id *match) +{ + const u32 *regaddr_p; + u64 regaddr64, size64; + s16 id = -1; + + regaddr_p = of_get_address(op->node, 0, &size64, NULL); + if (!regaddr_p) { + printk(KERN_ERR "Invalid PSC address\n"); + return -EINVAL; + } + regaddr64 = of_translate_address(op->node, regaddr_p); + + if (op->dev.platform_data == NULL) { + struct device_node *np; + int i = 0; + + for_each_node_by_type(np, "spi") { + if (of_find_device_by_node(np) == op) { + id = i; + break; + } + i++; + } + } + + return mpc52xx_psc_spi_do_probe(&op->dev, (u32)regaddr64, (u32)size64, + irq_of_parse_and_map(op->node, 0), id); +} + +static int __exit mpc52xx_psc_spi_of_remove(struct of_device *op) +{ + return mpc52xx_psc_spi_do_remove(&op->dev); +} + +static struct of_device_id mpc52xx_psc_spi_of_match[] = { + { .type = "spi", .compatible = "mpc52xx-psc-spi", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, mpc52xx_psc_spi_of_match); + +static struct of_platform_driver mpc52xx_psc_spi_of_driver = { + .owner = THIS_MODULE, + .name = "mpc52xx-psc-spi", + .match_table = mpc52xx_psc_spi_of_match, + .probe = mpc52xx_psc_spi_of_probe, + .remove = __exit_p(mpc52xx_psc_spi_of_remove), + .driver = { + .name = "mpc52xx-psc-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init mpc52xx_psc_spi_init(void) +{ + return of_register_platform_driver(&mpc52xx_psc_spi_of_driver); +} +module_init(mpc52xx_psc_spi_init); + +static void __exit mpc52xx_psc_spi_exit(void) +{ + of_unregister_platform_driver(&mpc52xx_psc_spi_of_driver); +} +module_exit(mpc52xx_psc_spi_exit); + +#endif /* defined(CONFIG_PPC_MERGE) */ + +MODULE_AUTHOR("Dragos Carp"); +MODULE_DESCRIPTION("MPC52xx PSC SPI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/telephony/Kconfig b/drivers/telephony/Kconfig index 7625b1816baf..dd1d6a53f3c0 100644 --- a/drivers/telephony/Kconfig +++ b/drivers/telephony/Kconfig @@ -3,6 +3,7 @@ # menu "Telephony Support" + depends on HAS_IOMEM config PHONE tristate "Linux telephony support" diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 278a22cea5bf..15499b7e33f4 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -3,6 +3,7 @@ # menu "USB support" + depends on HAS_IOMEM # Host-side USB depends on having a host controller # NOTE: dummy_hcd is always an option, but it's ignored here ... diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 9a256d2ff9dc..eebcb708cff1 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -3,6 +3,7 @@ # menu "Graphics support" + depends on HAS_IOMEM source "drivers/video/backlight/Kconfig" source "drivers/video/display/Kconfig" @@ -747,6 +748,22 @@ config FB_S1D13XXX working with S1D13806). Product specs at <http://www.erd.epson.com/vdc/html/legacy_13xxx.htm> +config FB_ATMEL + tristate "AT91/AT32 LCD Controller support" + depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || AVR32) + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This enables support for the AT91/AT32 LCD Controller. + +config FB_INTSRAM + bool "Frame Buffer in internal SRAM" + depends on FB_ATMEL && ARCH_AT91SAM9261 + help + Say Y if you want to map Frame Buffer in internal SRAM. Say N if you want + to let frame buffer in external SDRAM. + config FB_NVIDIA tristate "nVidia Framebuffer Support" depends on FB && PCI @@ -779,6 +796,15 @@ config FB_NVIDIA_I2C independently validate video mode parameters, you should say Y here. +config FB_NVIDIA_DEBUG + bool "Lots of debug output" + depends on FB_NVIDIA + default n + help + Say Y here if you want the nVidia driver to output all sorts + of debugging information to provide to the maintainer when + something goes wrong. + config FB_NVIDIA_BACKLIGHT bool "Support for backlight control" depends on FB_NVIDIA @@ -818,7 +844,7 @@ config FB_RIVA_I2C here. config FB_RIVA_DEBUG - bool "Lots of debug output from Riva(nVidia) driver" + bool "Lots of debug output" depends on FB_RIVA default n help @@ -1430,8 +1456,11 @@ config FB_ARK and ICS 5342 RAMDAC. config FB_PM3 - tristate "Permedia3 support" - depends on FB && PCI && BROKEN + tristate "Permedia3 support (EXPERIMENTAL)" + depends on FB && PCI && EXPERIMENTAL + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT help This is the frame buffer device driver for the 3DLabs Permedia3 chipset, used in Formac ProFormance III, 3DLabs Oxygen VX1 & diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 0b70567458fb..bd8b05229500 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_FB_G364) += g364fb.o obj-$(CONFIG_FB_SA1100) += sa1100fb.o obj-$(CONFIG_FB_HIT) += hitfb.o obj-$(CONFIG_FB_EPSON1355) += epson1355fb.o +obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c new file mode 100644 index 000000000000..e1d5bd0c98c4 --- /dev/null +++ b/drivers/video/atmel_lcdfb.c @@ -0,0 +1,752 @@ +/* + * Driver for AT91/AT32 LCD Controller + * + * Copyright (C) 2007 Atmel Corporation + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <asm/arch/board.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> + +#include <video/atmel_lcdc.h> + +#define lcdc_readl(sinfo, reg) __raw_readl((sinfo)->mmio+(reg)) +#define lcdc_writel(sinfo, reg, val) __raw_writel((val), (sinfo)->mmio+(reg)) + +/* configurable parameters */ +#define ATMEL_LCDC_CVAL_DEFAULT 0xc8 +#define ATMEL_LCDC_DMA_BURST_LEN 8 + +#if defined(CONFIG_ARCH_AT91SAM9263) +#define ATMEL_LCDC_FIFO_SIZE 2048 +#else +#define ATMEL_LCDC_FIFO_SIZE 512 +#endif + +#if defined(CONFIG_ARCH_AT91) +#define ATMEL_LCDFB_FBINFO_DEFAULT FBINFO_DEFAULT + +static inline void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo, + struct fb_var_screeninfo *var) +{ + +} +#elif defined(CONFIG_AVR32) +#define ATMEL_LCDFB_FBINFO_DEFAULT (FBINFO_DEFAULT \ + | FBINFO_PARTIAL_PAN_OK \ + | FBINFO_HWACCEL_XPAN \ + | FBINFO_HWACCEL_YPAN) + +static void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo, + struct fb_var_screeninfo *var) +{ + u32 dma2dcfg; + u32 pixeloff; + + pixeloff = (var->xoffset * var->bits_per_pixel) & 0x1f; + + dma2dcfg = ((var->xres_virtual - var->xres) * var->bits_per_pixel) / 8; + dma2dcfg |= pixeloff << ATMEL_LCDC_PIXELOFF_OFFSET; + lcdc_writel(sinfo, ATMEL_LCDC_DMA2DCFG, dma2dcfg); + + /* Update configuration */ + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, + lcdc_readl(sinfo, ATMEL_LCDC_DMACON) + | ATMEL_LCDC_DMAUPDT); +} +#endif + + +static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = { + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + + +static void atmel_lcdfb_update_dma(struct fb_info *info, + struct fb_var_screeninfo *var) +{ + struct atmel_lcdfb_info *sinfo = info->par; + struct fb_fix_screeninfo *fix = &info->fix; + unsigned long dma_addr; + + dma_addr = (fix->smem_start + var->yoffset * fix->line_length + + var->xoffset * var->bits_per_pixel / 8); + + dma_addr &= ~3UL; + + /* Set framebuffer DMA base address and pixel offset */ + lcdc_writel(sinfo, ATMEL_LCDC_DMABADDR1, dma_addr); + + atmel_lcdfb_update_dma2d(sinfo, var); +} + +static inline void atmel_lcdfb_free_video_memory(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + + dma_free_writecombine(info->device, info->fix.smem_len, + info->screen_base, info->fix.smem_start); +} + +/** + * atmel_lcdfb_alloc_video_memory - Allocate framebuffer memory + * @sinfo: the frame buffer to allocate memory for + */ +static int atmel_lcdfb_alloc_video_memory(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + struct fb_var_screeninfo *var = &info->var; + + info->fix.smem_len = (var->xres_virtual * var->yres_virtual + * ((var->bits_per_pixel + 7) / 8)); + + info->screen_base = dma_alloc_writecombine(info->device, info->fix.smem_len, + (dma_addr_t *)&info->fix.smem_start, GFP_KERNEL); + + if (!info->screen_base) { + return -ENOMEM; + } + + return 0; +} + +/** + * atmel_lcdfb_check_var - Validates a var passed in. + * @var: frame buffer variable screen structure + * @info: frame buffer structure that represents a single frame buffer + * + * Checks to see if the hardware supports the state requested by + * var passed in. This function does not alter the hardware + * state!!! This means the data stored in struct fb_info and + * struct atmel_lcdfb_info do not change. This includes the var + * inside of struct fb_info. Do NOT change these. This function + * can be called on its own if we intent to only test a mode and + * not actually set it. The stuff in modedb.c is a example of + * this. If the var passed in is slightly off by what the + * hardware can support then we alter the var PASSED in to what + * we can do. If the hardware doesn't support mode change a + * -EINVAL will be returned by the upper layers. You don't need + * to implement this function then. If you hardware doesn't + * support changing the resolution then this function is not + * needed. In this case the driver would just provide a var that + * represents the static state the screen is in. + * + * Returns negative errno on error, or zero on success. + */ +static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct device *dev = info->device; + struct atmel_lcdfb_info *sinfo = info->par; + unsigned long clk_value_khz; + + clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; + + dev_dbg(dev, "%s:\n", __func__); + dev_dbg(dev, " resolution: %ux%u\n", var->xres, var->yres); + dev_dbg(dev, " pixclk: %lu KHz\n", PICOS2KHZ(var->pixclock)); + dev_dbg(dev, " bpp: %u\n", var->bits_per_pixel); + dev_dbg(dev, " clk: %lu KHz\n", clk_value_khz); + + if ((PICOS2KHZ(var->pixclock) * var->bits_per_pixel / 8) > clk_value_khz) { + dev_err(dev, "%lu KHz pixel clock is too fast\n", PICOS2KHZ(var->pixclock)); + return -EINVAL; + } + + /* Force same alignment for each line */ + var->xres = (var->xres + 3) & ~3UL; + var->xres_virtual = (var->xres_virtual + 3) & ~3UL; + + var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0; + var->transp.msb_right = 0; + var->transp.offset = var->transp.length = 0; + var->xoffset = var->yoffset = 0; + + switch (var->bits_per_pixel) { + case 2: + case 4: + case 8: + var->red.offset = var->green.offset = var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length + = var->bits_per_pixel; + break; + case 15: + case 16: + var->red.offset = 0; + var->green.offset = 5; + var->blue.offset = 10; + var->red.length = var->green.length = var->blue.length = 5; + break; + case 24: + case 32: + var->red.offset = 0; + var->green.offset = 8; + var->blue.offset = 16; + var->red.length = var->green.length = var->blue.length = 8; + break; + default: + dev_err(dev, "color depth %d not supported\n", + var->bits_per_pixel); + return -EINVAL; + } + + return 0; +} + +/** + * atmel_lcdfb_set_par - Alters the hardware state. + * @info: frame buffer structure that represents a single frame buffer + * + * Using the fb_var_screeninfo in fb_info we set the resolution + * of the this particular framebuffer. This function alters the + * par AND the fb_fix_screeninfo stored in fb_info. It doesn't + * not alter var in fb_info since we are using that data. This + * means we depend on the data in var inside fb_info to be + * supported by the hardware. atmel_lcdfb_check_var is always called + * before atmel_lcdfb_set_par to ensure this. Again if you can't + * change the resolution you don't need this function. + * + */ +static int atmel_lcdfb_set_par(struct fb_info *info) +{ + struct atmel_lcdfb_info *sinfo = info->par; + unsigned long value; + unsigned long clk_value_khz; + + dev_dbg(info->device, "%s:\n", __func__); + dev_dbg(info->device, " * resolution: %ux%u (%ux%u virtual)\n", + info->var.xres, info->var.yres, + info->var.xres_virtual, info->var.yres_virtual); + + /* Turn off the LCD controller and the DMA controller */ + lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET); + + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0); + + if (info->var.bits_per_pixel <= 8) + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + + info->fix.line_length = info->var.xres_virtual * (info->var.bits_per_pixel / 8); + + /* Re-initialize the DMA engine... */ + dev_dbg(info->device, " * update DMA engine\n"); + atmel_lcdfb_update_dma(info, &info->var); + + /* ...set frame size and burst length = 8 words (?) */ + value = (info->var.yres * info->var.xres * info->var.bits_per_pixel) / 32; + value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET); + lcdc_writel(sinfo, ATMEL_LCDC_DMAFRMCFG, value); + + /* Now, the LCDC core... */ + + /* Set pixel clock */ + clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; + + value = clk_value_khz / PICOS2KHZ(info->var.pixclock); + + if (clk_value_khz % PICOS2KHZ(info->var.pixclock)) + value++; + + value = (value / 2) - 1; + + if (value <= 0) { + dev_notice(info->device, "Bypassing pixel clock divider\n"); + lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS); + } else + lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, value << ATMEL_LCDC_CLKVAL_OFFSET); + + /* Initialize control register 2 */ + value = sinfo->default_lcdcon2; + + if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) + value |= ATMEL_LCDC_INVLINE_INVERTED; + if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) + value |= ATMEL_LCDC_INVFRAME_INVERTED; + + switch (info->var.bits_per_pixel) { + case 1: value |= ATMEL_LCDC_PIXELSIZE_1; break; + case 2: value |= ATMEL_LCDC_PIXELSIZE_2; break; + case 4: value |= ATMEL_LCDC_PIXELSIZE_4; break; + case 8: value |= ATMEL_LCDC_PIXELSIZE_8; break; + case 15: /* fall through */ + case 16: value |= ATMEL_LCDC_PIXELSIZE_16; break; + case 24: value |= ATMEL_LCDC_PIXELSIZE_24; break; + case 32: value |= ATMEL_LCDC_PIXELSIZE_32; break; + default: BUG(); break; + } + dev_dbg(info->device, " * LCDCON2 = %08lx\n", value); + lcdc_writel(sinfo, ATMEL_LCDC_LCDCON2, value); + + /* Vertical timing */ + value = (info->var.vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET; + value |= info->var.upper_margin << ATMEL_LCDC_VBP_OFFSET; + value |= info->var.lower_margin; + dev_dbg(info->device, " * LCDTIM1 = %08lx\n", value); + lcdc_writel(sinfo, ATMEL_LCDC_TIM1, value); + + /* Horizontal timing */ + value = (info->var.right_margin - 1) << ATMEL_LCDC_HFP_OFFSET; + value |= (info->var.hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET; + value |= (info->var.left_margin - 1); + dev_dbg(info->device, " * LCDTIM2 = %08lx\n", value); + lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value); + + /* Display size */ + value = (info->var.xres - 1) << ATMEL_LCDC_HOZVAL_OFFSET; + value |= info->var.yres - 1; + lcdc_writel(sinfo, ATMEL_LCDC_LCDFRMCFG, value); + + /* FIFO Threshold: Use formula from data sheet */ + value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3); + lcdc_writel(sinfo, ATMEL_LCDC_FIFO, value); + + /* Toggle LCD_MODE every frame */ + lcdc_writel(sinfo, ATMEL_LCDC_MVAL, 0); + + /* Disable all interrupts */ + lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); + + /* Set contrast */ + value = ATMEL_LCDC_PS_DIV8 | ATMEL_LCDC_POL_POSITIVE | ATMEL_LCDC_ENA_PWMENABLE; + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, value); + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT); + /* ...wait for DMA engine to become idle... */ + while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY) + msleep(10); + + dev_dbg(info->device, " * re-enable DMA engine\n"); + /* ...and enable it with updated configuration */ + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon); + + dev_dbg(info->device, " * re-enable LCDC core\n"); + lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, + (sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR); + + dev_dbg(info->device, " * DONE\n"); + + return 0; +} + +static inline unsigned int chan_to_field(unsigned int chan, const struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +/** + * atmel_lcdfb_setcolreg - Optional function. Sets a color register. + * @regno: Which register in the CLUT we are programming + * @red: The red value which can be up to 16 bits wide + * @green: The green value which can be up to 16 bits wide + * @blue: The blue value which can be up to 16 bits wide. + * @transp: If supported the alpha value which can be up to 16 bits wide. + * @info: frame buffer info structure + * + * Set a single color register. The values supplied have a 16 bit + * magnitude which needs to be scaled in this function for the hardware. + * Things to take into consideration are how many color registers, if + * any, are supported with the current color visual. With truecolor mode + * no color palettes are supported. Here a psuedo palette is created + * which we store the value in pseudo_palette in struct fb_info. For + * pseudocolor mode we have a limited color palette. To deal with this + * we can program what color is displayed for a particular pixel value. + * DirectColor is similar in that we can program each color field. If + * we have a static colormap we don't need to implement this function. + * + * Returns negative errno on error, or zero on success. In an + * ideal world, this would have been the case, but as it turns + * out, the other drivers return 1 on failure, so that's what + * we're going to do. + */ +static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int transp, struct fb_info *info) +{ + struct atmel_lcdfb_info *sinfo = info->par; + unsigned int val; + u32 *pal; + int ret = 1; + + if (info->var.grayscale) + red = green = blue = (19595 * red + 38470 * green + + 7471 * blue) >> 16; + + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + if (regno < 16) { + pal = info->pseudo_palette; + + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue, &info->var.blue); + + pal[regno] = val; + ret = 0; + } + break; + + case FB_VISUAL_PSEUDOCOLOR: + if (regno < 256) { + val = ((red >> 11) & 0x001f); + val |= ((green >> 6) & 0x03e0); + val |= ((blue >> 1) & 0x7c00); + + /* + * TODO: intensity bit. Maybe something like + * ~(red[10] ^ green[10] ^ blue[10]) & 1 + */ + + lcdc_writel(sinfo, ATMEL_LCDC_LUT(regno), val); + ret = 0; + } + break; + } + + return ret; +} + +static int atmel_lcdfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + dev_dbg(info->device, "%s\n", __func__); + + atmel_lcdfb_update_dma(info, var); + + return 0; +} + +static struct fb_ops atmel_lcdfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = atmel_lcdfb_check_var, + .fb_set_par = atmel_lcdfb_set_par, + .fb_setcolreg = atmel_lcdfb_setcolreg, + .fb_pan_display = atmel_lcdfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static irqreturn_t atmel_lcdfb_interrupt(int irq, void *dev_id) +{ + struct fb_info *info = dev_id; + struct atmel_lcdfb_info *sinfo = info->par; + u32 status; + + status = lcdc_readl(sinfo, ATMEL_LCDC_ISR); + lcdc_writel(sinfo, ATMEL_LCDC_IDR, status); + return IRQ_HANDLED; +} + +static int __init atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + int ret = 0; + + memset_io(info->screen_base, 0, info->fix.smem_len); + info->var.activate |= FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; + + dev_info(info->device, + "%luKiB frame buffer at %08lx (mapped at %p)\n", + (unsigned long)info->fix.smem_len / 1024, + (unsigned long)info->fix.smem_start, + info->screen_base); + + /* Allocate colormap */ + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret < 0) + dev_err(info->device, "Alloc color map failed\n"); + + return ret; +} + +static void atmel_lcdfb_start_clock(struct atmel_lcdfb_info *sinfo) +{ + if (sinfo->bus_clk) + clk_enable(sinfo->bus_clk); + clk_enable(sinfo->lcdc_clk); +} + +static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo) +{ + if (sinfo->bus_clk) + clk_disable(sinfo->bus_clk); + clk_disable(sinfo->lcdc_clk); +} + + +static int __init atmel_lcdfb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fb_info *info; + struct atmel_lcdfb_info *sinfo; + struct atmel_lcdfb_info *pdata_sinfo; + struct resource *regs = NULL; + struct resource *map = NULL; + int ret; + + dev_dbg(dev, "%s BEGIN\n", __func__); + + ret = -ENOMEM; + info = framebuffer_alloc(sizeof(struct atmel_lcdfb_info), dev); + if (!info) { + dev_err(dev, "cannot allocate memory\n"); + goto out; + } + + sinfo = info->par; + + if (dev->platform_data) { + pdata_sinfo = (struct atmel_lcdfb_info *)dev->platform_data; + sinfo->default_bpp = pdata_sinfo->default_bpp; + sinfo->default_dmacon = pdata_sinfo->default_dmacon; + sinfo->default_lcdcon2 = pdata_sinfo->default_lcdcon2; + sinfo->default_monspecs = pdata_sinfo->default_monspecs; + sinfo->atmel_lcdfb_power_control = pdata_sinfo->atmel_lcdfb_power_control; + sinfo->guard_time = pdata_sinfo->guard_time; + } else { + dev_err(dev, "cannot get default configuration\n"); + goto free_info; + } + sinfo->info = info; + sinfo->pdev = pdev; + + strcpy(info->fix.id, sinfo->pdev->name); + info->flags = ATMEL_LCDFB_FBINFO_DEFAULT; + info->pseudo_palette = sinfo->pseudo_palette; + info->fbops = &atmel_lcdfb_ops; + + memcpy(&info->monspecs, sinfo->default_monspecs, sizeof(info->monspecs)); + info->fix = atmel_lcdfb_fix; + + /* Enable LCDC Clocks */ + if (cpu_is_at91sam9261() || cpu_is_at32ap7000()) { + sinfo->bus_clk = clk_get(dev, "hck1"); + if (IS_ERR(sinfo->bus_clk)) { + ret = PTR_ERR(sinfo->bus_clk); + goto free_info; + } + } + sinfo->lcdc_clk = clk_get(dev, "lcdc_clk"); + if (IS_ERR(sinfo->lcdc_clk)) { + ret = PTR_ERR(sinfo->lcdc_clk); + goto put_bus_clk; + } + atmel_lcdfb_start_clock(sinfo); + + ret = fb_find_mode(&info->var, info, NULL, info->monspecs.modedb, + info->monspecs.modedb_len, info->monspecs.modedb, + sinfo->default_bpp); + if (!ret) { + dev_err(dev, "no suitable video mode found\n"); + goto stop_clk; + } + + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(dev, "resources unusable\n"); + ret = -ENXIO; + goto stop_clk; + } + + sinfo->irq_base = platform_get_irq(pdev, 0); + if (sinfo->irq_base < 0) { + dev_err(dev, "unable to get irq\n"); + ret = sinfo->irq_base; + goto stop_clk; + } + + /* Initialize video memory */ + map = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (map) { + /* use a pre-allocated memory buffer */ + info->fix.smem_start = map->start; + info->fix.smem_len = map->end - map->start + 1; + if (!request_mem_region(info->fix.smem_start, + info->fix.smem_len, pdev->name)) { + ret = -EBUSY; + goto stop_clk; + } + + info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); + if (!info->screen_base) + goto release_intmem; + } else { + /* alocate memory buffer */ + ret = atmel_lcdfb_alloc_video_memory(sinfo); + if (ret < 0) { + dev_err(dev, "cannot allocate framebuffer: %d\n", ret); + goto stop_clk; + } + } + + /* LCDC registers */ + info->fix.mmio_start = regs->start; + info->fix.mmio_len = regs->end - regs->start + 1; + + if (!request_mem_region(info->fix.mmio_start, + info->fix.mmio_len, pdev->name)) { + ret = -EBUSY; + goto free_fb; + } + + sinfo->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len); + if (!sinfo->mmio) { + dev_err(dev, "cannot map LCDC registers\n"); + goto release_mem; + } + + /* interrupt */ + ret = request_irq(sinfo->irq_base, atmel_lcdfb_interrupt, 0, pdev->name, info); + if (ret) { + dev_err(dev, "request_irq failed: %d\n", ret); + goto unmap_mmio; + } + + ret = atmel_lcdfb_init_fbinfo(sinfo); + if (ret < 0) { + dev_err(dev, "init fbinfo failed: %d\n", ret); + goto unregister_irqs; + } + + /* + * This makes sure that our colour bitfield + * descriptors are correctly initialised. + */ + atmel_lcdfb_check_var(&info->var, info); + + ret = fb_set_var(info, &info->var); + if (ret) { + dev_warn(dev, "unable to set display parameters\n"); + goto free_cmap; + } + + dev_set_drvdata(dev, info); + + /* + * Tell the world that we're ready to go + */ + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(dev, "failed to register framebuffer device: %d\n", ret); + goto free_cmap; + } + + /* Power up the LCDC screen */ + if (sinfo->atmel_lcdfb_power_control) + sinfo->atmel_lcdfb_power_control(1); + + dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %lu\n", + info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base); + + return 0; + + +free_cmap: + fb_dealloc_cmap(&info->cmap); +unregister_irqs: + free_irq(sinfo->irq_base, info); +unmap_mmio: + iounmap(sinfo->mmio); +release_mem: + release_mem_region(info->fix.mmio_start, info->fix.mmio_len); +free_fb: + if (map) + iounmap(info->screen_base); + else + atmel_lcdfb_free_video_memory(sinfo); + +release_intmem: + if (map) + release_mem_region(info->fix.smem_start, info->fix.smem_len); +stop_clk: + atmel_lcdfb_stop_clock(sinfo); + clk_put(sinfo->lcdc_clk); +put_bus_clk: + if (sinfo->bus_clk) + clk_put(sinfo->bus_clk); +free_info: + framebuffer_release(info); +out: + dev_dbg(dev, "%s FAILED\n", __func__); + return ret; +} + +static int __exit atmel_lcdfb_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fb_info *info = dev_get_drvdata(dev); + struct atmel_lcdfb_info *sinfo = info->par; + + if (!sinfo) + return 0; + + if (sinfo->atmel_lcdfb_power_control) + sinfo->atmel_lcdfb_power_control(0); + unregister_framebuffer(info); + atmel_lcdfb_stop_clock(sinfo); + clk_put(sinfo->lcdc_clk); + if (sinfo->bus_clk) + clk_put(sinfo->bus_clk); + fb_dealloc_cmap(&info->cmap); + free_irq(sinfo->irq_base, info); + iounmap(sinfo->mmio); + release_mem_region(info->fix.mmio_start, info->fix.mmio_len); + if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) { + iounmap(info->screen_base); + release_mem_region(info->fix.smem_start, info->fix.smem_len); + } else { + atmel_lcdfb_free_video_memory(sinfo); + } + + dev_set_drvdata(dev, NULL); + framebuffer_release(info); + + return 0; +} + +static struct platform_driver atmel_lcdfb_driver = { + .remove = __exit_p(atmel_lcdfb_remove), + .driver = { + .name = "atmel_lcdfb", + .owner = THIS_MODULE, + }, +}; + +static int __init atmel_lcdfb_init(void) +{ + return platform_driver_probe(&atmel_lcdfb_driver, atmel_lcdfb_probe); +} + +static void __exit atmel_lcdfb_exit(void) +{ + platform_driver_unregister(&atmel_lcdfb_driver); +} + +module_init(atmel_lcdfb_init); +module_exit(atmel_lcdfb_exit); + +MODULE_DESCRIPTION("AT91/AT32 LCD Controller framebuffer driver"); +MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@rfo.atmel.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index aa3935df852a..63b85bf81a65 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -19,13 +19,6 @@ config VGA_CONSOLE Say Y. -# if [ "$CONFIG_PCI" = "y" -a "$CONFIG_VGA_CONSOLE" = "y" ]; then -# bool ' Allow VGA on any bus?' CONFIG_VGA_HOSE -# if [ "$CONFIG_VGA_HOSE" = "y" ]; then -# define_bool CONFIG_DUMMY_CONSOLE y -# fi -# fi - config VGACON_SOFT_SCROLLBACK bool "Enable Scrollback Buffer in System RAM" depends on VGA_CONSOLE diff --git a/drivers/video/nvidia/nv_hw.c b/drivers/video/nvidia/nv_hw.c index c627955aa124..aff11bbf59a7 100644 --- a/drivers/video/nvidia/nv_hw.c +++ b/drivers/video/nvidia/nv_hw.c @@ -149,7 +149,9 @@ static void nvGetClocks(struct nvidia_par *par, unsigned int *MClk, pll = NV_RD32(par->PMC, 0x4024); M = pll & 0xFF; N = (pll >> 8) & 0xFF; - if (((par->Chipset & 0xfff0) == 0x0290) || ((par->Chipset & 0xfff0) == 0x0390) || ((par->Chipset & 0xfff0) == 0x02E0)) { + if (((par->Chipset & 0xfff0) == 0x0290) || + ((par->Chipset & 0xfff0) == 0x0390) || + ((par->Chipset & 0xfff0) == 0x02E0)) { MB = 1; NB = 1; } else { diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index f85edf084da3..41f63658572f 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -37,7 +37,6 @@ #include "nv_proto.h" #include "nv_dma.h" -#undef CONFIG_FB_NVIDIA_DEBUG #ifdef CONFIG_FB_NVIDIA_DEBUG #define NVTRACE printk #else diff --git a/drivers/video/pm2fb.c b/drivers/video/pm2fb.c index 1ac5264bb2c1..ab5e66890e4e 100644 --- a/drivers/video/pm2fb.c +++ b/drivers/video/pm2fb.c @@ -204,17 +204,6 @@ static inline void WAIT_FIFO(struct pm2fb_par* p, u32 a) } #endif -static void wait_pm2(struct pm2fb_par* par) { - - WAIT_FIFO(par, 1); - pm2_WR(par, PM2R_SYNC, 0); - mb(); - do { - while (pm2_RD(par, PM2R_OUT_FIFO_WORDS) == 0); - rmb(); - } while (pm2_RD(par, PM2R_OUT_FIFO) != PM2TAG(PM2R_SYNC)); -} - /* * partial products for the supported horizontal resolutions. */ @@ -1050,13 +1039,30 @@ static int pm2fb_blank(int blank_mode, struct fb_info *info) return 0; } +static int pm2fb_sync(struct fb_info *info) +{ + struct pm2fb_par *par = info->par; + + WAIT_FIFO(par, 1); + pm2_WR(par, PM2R_SYNC, 0); + mb(); + do { + while (pm2_RD(par, PM2R_OUT_FIFO_WORDS) == 0) + udelay(10); + rmb(); + } while (pm2_RD(par, PM2R_OUT_FIFO) != PM2TAG(PM2R_SYNC)); + + return 0; +} + /* * block operation. copy=0: rectangle fill, copy=1: rectangle copy. */ -static void pm2fb_block_op(struct pm2fb_par* par, int copy, +static void pm2fb_block_op(struct fb_info* info, int copy, s32 xsrc, s32 ysrc, s32 x, s32 y, s32 w, s32 h, u32 color) { + struct pm2fb_par *par = info->par; if (!w || !h) return; @@ -1076,13 +1082,11 @@ static void pm2fb_block_op(struct pm2fb_par* par, int copy, (x<xsrc ? PM2F_INCREASE_X : 0) | (y<ysrc ? PM2F_INCREASE_Y : 0) | (copy ? 0 : PM2F_RENDER_FASTFILL)); - wait_pm2(par); } static void pm2fb_fillrect (struct fb_info *info, const struct fb_fillrect *region) { - struct pm2fb_par *par = info->par; struct fb_fillrect modded; int vxres, vyres; u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ? @@ -1116,7 +1120,7 @@ static void pm2fb_fillrect (struct fb_info *info, color |= color << 16; if(info->var.bits_per_pixel != 24) - pm2fb_block_op(par, 0, 0, 0, + pm2fb_block_op(info, 0, 0, 0, modded.dx, modded.dy, modded.width, modded.height, color); else @@ -1126,7 +1130,6 @@ static void pm2fb_fillrect (struct fb_info *info, static void pm2fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) { - struct pm2fb_par *par = info->par; struct fb_copyarea modded; u32 vxres, vyres; @@ -1156,7 +1159,7 @@ static void pm2fb_copyarea(struct fb_info *info, if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy; - pm2fb_block_op(par, 1, modded.sx, modded.sy, + pm2fb_block_op(info, 1, modded.sx, modded.sy, modded.dx, modded.dy, modded.width, modded.height, 0); } @@ -1177,6 +1180,7 @@ static struct fb_ops pm2fb_ops = { .fb_fillrect = pm2fb_fillrect, .fb_copyarea = pm2fb_copyarea, .fb_imageblit = cfb_imageblit, + .fb_sync = pm2fb_sync, }; /* diff --git a/drivers/video/pm3fb.c b/drivers/video/pm3fb.c index bd787e80177d..6c4dfcb0feb9 100644 --- a/drivers/video/pm3fb.c +++ b/drivers/video/pm3fb.c @@ -1,55 +1,25 @@ /* * linux/drivers/video/pm3fb.c -- 3DLabs Permedia3 frame buffer device - * - * Copyright (C) 2001 Romain Dolbeau <dolbeau@irisa.fr> + * + * Copyright (C) 2001 Romain Dolbeau <romain@dolbeau.org>. + * + * Ported to 2.6 kernel on 1 May 2007 by Krzysztof Helt <krzysztof.h1@wp.pl> + * based on pm2fb.c + * * Based on code written by: - * Sven Luther, <luther@dpt-info.u-strasbg.fr> - * Alan Hourihane, <alanh@fairlite.demon.co.uk> - * Russell King, <rmk@arm.linux.org.uk> + * Sven Luther, <luther@dpt-info.u-strasbg.fr> + * Alan Hourihane, <alanh@fairlite.demon.co.uk> + * Russell King, <rmk@arm.linux.org.uk> * Based on linux/drivers/video/skeletonfb.c: * Copyright (C) 1997 Geert Uytterhoeven * Based on linux/driver/video/pm2fb.c: - * Copyright (C) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) - * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com) + * Copyright (C) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) + * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com) * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. * - * $Header: /cvsroot/linux/drivers/video/pm3fb.c,v 1.1 2002/02/25 19:11:06 marcelo Exp $ - * - * CHANGELOG: - * Mon Feb 11 10:35:48 MET 2002, v 1.4.11B: Cosmetic update. - * Wed Jan 23 14:16:59 MET 2002, v 1.4.11: Preliminary 2.5.x support, patch for 2.5.2. - * Wed Nov 28 11:08:29 MET 2001, v 1.4.10: potential bug fix for SDRAM-based board, patch for 2.4.16. - * Thu Sep 20 10:24:42 MET DST 2001, v 1.4.9: sync bug fix, preliminary flatpanel support, better timings. - * Tue Aug 28 10:13:01 MET DST 2001, v 1.4.8: memory timings check, minor bug fixes. - * Wed Jul 18 19:06:14 CEST 2001, v 1.4.7: Mode fix (800x600-100, 1024x768-100 changed), using HW panning + accel bug fix. - * Mon Jun 25 10:33:56 MET DST 2001, v 1.4.6: Depth 12 fix, chip reset ioctl, moved memory erase ioctl to DEBUG. - * Wed Jun 20 11:13:08 MET DST 2001, v 1.4.5: Fixed missing blinking cursor in 8bpp, code cleaning, memory erase IOCTL. - * Mon Jun 18 16:00:27 CEST 2001, v 1.4.4: Depth 12 (RGBA 4444) support, code cleaning. - * Fri Jun 15 13:53:01 CEST 2001, v 1.4.3: Removed warnings, depth 15 support, add 'depth' option. - * Thu Jun 14 10:13:52 MET DST 2001, v 1.4.2: Fixed depth switching bug, preliminary 15bpp (RGB5551) support. - * Thu Apr 12 11:16:45 MET DST 2001, v 1.4.1B: Doc updates. - * Fri Apr 6 11:12:53 MET DST 2001, v 1.4.1: Configure.help, minor cleanup - * Thu Mar 29 10:56:50 MET DST 2001, v 1.4.0: Module & module options support (note: linux patch changed, 2.2.19 added). - * Thu Mar 15 15:30:31 MET 2001, v 1.3.2: Fixed mirroring bug on little-endian. - * Wed Mar 14 21:25:54 CET 2001, v 1.3.1: Fixed bug in BlockMove (_bmov). - * Tue Mar 13 10:53:19 MET 2001, v 1.3.0: Character drawing hardware support (in all width between 1 and 16), fixes. - * Thu Mar 8 10:20:16 MET 2001, v 1.2.2: Better J2000 support, "font:" option. - * Tue Mar 6 21:25:04 CET 2001, v 1.2.1: Better acceleration support. - * Mon Mar 5 21:54:17 CET 2001, v 1.2.0: Partial acceleration support (clear & bmove) - * Mon Mar 5 12:52:15 CET 2001, v 1.1.3: Big pan_display fix. - * Sun Mar 4 22:21:50 CET 2001, v 1.1.2: (numerous) bug fixes. - * Fri Mar 2 15:54:07 CET 2001, v 1.1.1: Might have Appian J2000 support, resource mangement in 2.4 - * Wed Feb 28 18:21:35 CET 2001, v 1.1.0: Might have multiple boards support (added, but not yest tested) - * Tue Feb 27 17:31:12 CET 2001, v 1.0.6: fixes boot-time mode select, add more default mode - * Tue Feb 27 14:01:36 CET 2001, v 1.0.5: fixes (1.0.4 was broken for 2.2), cleaning up - * Mon Feb 26 23:17:36 CET 2001, v 1.0.4: preliminary 2.4.x support, dropped (useless on pm3) partial product, more OF fix - * Mon Feb 26 20:59:05 CET 2001, v 1.0.3: No more shadow register (and wasted memory), endianess fix, use OF-preset resolution by default - * Wed Feb 21 22:09:30 CET 2001, v 1.0.2: Code cleaning for future multiboard support, better OF support, bugs fix - * Wed Feb 21 19:58:56 CET 2001, v 1.0.1: OpenFirmware support, fixed memory detection, better debug support, code cleaning - * Wed Feb 21 14:47:06 CET 2001, v 1.0.0: First working version */ #include <linux/module.h> @@ -58,856 +28,155 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/slab.h> -#include <linux/vmalloc.h> #include <linux/delay.h> -#include <linux/interrupt.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/pci.h> -#include <linux/ioport.h> -#include <linux/ctype.h> - -#include <video/fbcon.h> -#include <video/fbcon-mfb.h> -#include <video/fbcon-cfb2.h> -#include <video/fbcon-cfb4.h> -#include <video/fbcon-cfb8.h> -#include <video/fbcon-cfb16.h> -#include <video/fbcon-cfb24.h> -#include <video/fbcon-cfb32.h> -#include <video/pm3fb.h> -#include <asm/io.h> -#include <asm/uaccess.h> +#include <video/pm3fb.h> -#ifdef CONFIG_FB_OF -#include <asm/prom.h> +#if !defined(CONFIG_PCI) +#error "Only generic PCI cards supported." #endif -/* ************************************* */ -/* ***** The various "global" data ***** */ -/* ************************************* */ - -/* those will need a rework for multiple board support */ -/* Driver name */ -static const char permedia3_name[16] = "Permedia3"; - -/* the fb_par struct, mandatory */ -struct pm3fb_par { - u32 pixclock; /* pixclock in KHz */ - - u32 width; /* width of virtual screen */ - u32 height; /* height of virtual screen */ - - u32 hsstart; /* horiz. sync start */ - u32 hsend; /* horiz. sync end */ - u32 hbend; /* horiz. blank end (also gate end) */ - u32 htotal; /* total width (w/ sync & blank) */ - - u32 vsstart; /* vert. sync start */ - u32 vsend; /* vert. sync end */ - u32 vbend; /* vert. blank end */ - u32 vtotal; /* total height (w/ sync & blank) */ - - u32 stride; /* screen stride */ - u32 base; /* screen base (xoffset+yoffset) in 128 bits unit */ - /* NOTE : unlike other pm3 stuff above, stored *after* shiftbpp. don't ask */ - u32 depth; /* screen depth (8, 12, 15, 16 or 32) */ - u32 video; /* video control (hsync,vsync) */ -}; - -/* memory timings */ -struct pm3fb_timings -{ - unsigned long caps; - unsigned long timings; - unsigned long control; - unsigned long refresh; - unsigned long powerdown; -}; -typedef enum pm3fb_timing_result { pm3fb_timing_ok, pm3fb_timing_problem, pm3fb_timing_retry } pm3fb_timing_result; -#define PM3FB_UNKNOWN_TIMING_VALUE ((unsigned long)-1) -#define PM3FB_UNKNOWN_TIMINGS { PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE } - -/* the fb_info struct, mandatory */ -struct pm3fb_info { - struct fb_info_gen gen; - unsigned long board_num; /* internal board number */ - unsigned long use_current; - struct pm3fb_par *current_par; - struct pci_dev *dev; /* PCI device */ - unsigned long board_type; /* index in the cardbase */ - unsigned char *fb_base; /* framebuffer memory base */ - u32 fb_size; /* framebuffer memory size */ - unsigned char *p_fb; /* physical address of frame buffer */ - unsigned char *v_fb; /* virtual address of frame buffer */ - unsigned char *pIOBase; /* physical address of registers region, must be rg_base or rg_base+PM2_REGS_SIZE depending on the host endianness */ - unsigned char *vIOBase; /* address of registers after ioremap() */ - struct { - u8 transp; - u8 red; - u8 green; - u8 blue; - } palette[256]; - union { -#ifdef FBCON_HAS_CFB16 - u16 cmap12[16]; /* RGBA 4444 */ - u16 cmap15[16]; /* RGBA 5551 */ - u16 cmap16[16]; /* RGBA 5650 */ -#endif -#ifdef FBCON_HAS_CFB32 - u32 cmap32[16]; +#undef PM3FB_MASTER_DEBUG +#ifdef PM3FB_MASTER_DEBUG +#define DPRINTK(a,b...) printk(KERN_DEBUG "pm3fb: %s: " a, __FUNCTION__ , ## b) +#else +#define DPRINTK(a,b...) #endif - } cmap; - struct pm3fb_timings memt; -}; - -/* regular resolution database*/ -static struct { - char name[16]; - struct pm3fb_par user_mode; -} mode_base[] __initdata = { - { - "default-800x600", { - 49500, 800, 600, 16, 96, 256, 1056, 1, 4, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, { - "1024x768-74", { - 78752, 1024, 768, 32, 128, 304, 1328, 1, 4, 38, - 806, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, { - "1024x768-74-32", { - 78752, 1024, 768, 32, 128, 304, 1328, 1, 4, 38, - 806, 1024, 0, 32, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_32BIT}}, -/* Generated mode : "1600x1024", for the SGI 1600SW flat panel*/ - { - "SGI1600SW", { - 108000, 1600, 1024, 16, 56, 104, 1704, 3, 6, 32, - 1056, 1600, 0, 8, - PM3VideoControl_ENABLE| - PM3VideoControl_HSYNC_ACTIVE_LOW|PM3VideoControl_VSYNC_ACTIVE_LOW| - PM3VideoControl_PIXELSIZE_32BIT}}, -/* ##### auto-generated mode, by fbtimings2pm3 */ -/* Generated mode : "640x480-60" */ - { - "640x480-60", { - 25174, 640, 480, 16, 112, 160, 800, 10, 12, 45, - 525, 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-72" */ - { - "640x480-72", { - 31199, 640, 480, 24, 64, 192, 832, 9, 12, 40, 520, - 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-75" */ - { - "640x480-75", { - 31499, 640, 480, 16, 80, 200, 840, 1, 4, 20, 500, - 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-90" */ - { - "640x480-90", { - 39909, 640, 480, 32, 72, 192, 832, 25, 39, 53, 533, - 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-100" */ - { - "640x480-100", { - 44899, 640, 480, 32, 160, 208, 848, 22, 34, 51, - 531, 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-48-lace" */ -/* INTERLACED NOT SUPPORTED - {"800x600-48-lace", {35999, 800, 600, 80, 208, 264, 1064, 11, 23, 102, 702, 800, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "800x600-56" */ - { - "800x600-56", { - 35999, 800, 600, 24, 96, 224, 1024, 1, 3, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-60" */ - { - "800x600-60", { - 40000, 800, 600, 40, 168, 256, 1056, 1, 5, 28, 628, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-70" */ - { - "800x600-70", { - 44899, 800, 600, 24, 168, 208, 1008, 9, 21, 36, - 636, 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-72" */ - { - "800x600-72", { - 50000, 800, 600, 56, 176, 240, 1040, 37, 43, 66, - 666, 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-75" */ - { - "800x600-75", { - 49497, 800, 600, 16, 96, 256, 1056, 1, 4, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-90" */ - { - "800x600-90", { - 56637, 800, 600, 8, 72, 192, 992, 8, 19, 35, 635, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-100", from /etc/fb.modes */ -/* DISABLED, hsstart == 0 - { - "800x600-100", { - 67499, 800, 600, 0, 64, 280, 1080, 7, 11, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -*/ -/* Generated mode : "800x600-100", from ??? */ - { - "800x600-100", { - 69650, 800, 600, 64, 128, 288, 1088, 4, 10, 40, 640, 800, 0, 8, - PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_LOW| - PM3VideoControl_VSYNC_ACTIVE_LOW|PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-43-lace" */ -/* INTERLACED NOT SUPPORTED - {"1024x768-43-lace", {44899, 1024, 768, 8, 184, 240, 1264, 1, 9, 49, 817, 1024, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1024x768-60" */ - { - "1024x768-60", { - 64998, 1024, 768, 24, 160, 320, 1344, 3, 9, 38, - 806, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-70" */ - { - "1024x768-70", { - 74996, 1024, 768, 24, 160, 304, 1328, 3, 9, 38, - 806, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-72" */ - { - "1024x768-72", { - 74996, 10224, 768, 24, 160, 264, 10488, 3, 9, 38, - 806, 10224, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-75" */ - { - "1024x768-75", { - 78746, 1024, 768, 16, 112, 288, 1312, 1, 4, 32, - 800, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-90" */ - { - "1024x768-90", { - 100000, 1024, 768, 0, 96, 288, 1312, 21, 36, 77, - 845, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-100", from /etc/fb.modes */ -/* DISABLED, vsstart == 0 - { - "1024x768-100", { - 109998, 1024, 768, 0, 88, 368, 1392, 0, 8, 24, 792, - 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -*/ -/* Generated mode : "1024x768-100", from ??? */ - { - "1024x768-100", { - 115500, 1024, 768, 32, 224, 416, 1440, 3, 13, 34, 802, 1024, 0, 8, - PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_LOW| - PM3VideoControl_VSYNC_ACTIVE_LOW|PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-43-lace" */ -/* INTERLACED NOT SUPPORTED - {"1152x864-43-lace", {64998, 1152, 864, 72, 200, 264, 1416, 78, 87, 191, 1055, 1152, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1152x864-47-lace" */ -/* INTERLACED NOT SUPPORTED - {"1152x864-47-lace", {64998, 1152, 864, 88, 216, 296, 1448, 30, 39, 83, 947, 1152, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1152x864-60" */ - { - "1152x864-60", { - 80000, 1152, 864, 64, 176, 304, 1456, 6, 11, 52, - 916, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-70" */ - { - "1152x864-70", { - 100000, 1152, 864, 40, 192, 360, 1512, 13, 24, 81, - 945, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-75" */ - { - "1152x864-75", { - 109998, 1152, 864, 24, 168, 312, 1464, 45, 53, 138, - 1002, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-80" */ - { - "1152x864-80", { - 109998, 1152, 864, 16, 128, 288, 1440, 30, 37, 94, - 958, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-43-lace" */ -/* INTERLACED NOT SUPPORTED - {"1280x1024-43-lace", {80000, 1024, 1024, 80, 160, 320, 1344, 50, 60, 125, 1149, 1024, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1280x1024-47-lace" */ -/* INTERLACED NOT SUPPORTED - {"1280x1024-47-lace", {80000, 1280, 1024, 80, 160, 320, 1600, 1, 11, 29, 1053, 1280, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1280x1024-60" */ - { - "1280x1024-60", { - 107991, 1280, 1024, 48, 160, 408, 1688, 1, 4, 42, - 1066, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-70" */ - { - "1280x1024-70", { - 125992, 1280, 1024, 80, 192, 408, 1688, 1, 6, 42, - 1066, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-74" */ - { - "1280x1024-74", { - 134989, 1280, 1024, 32, 176, 432, 1712, 0, 30, 40, - 1064, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-75" */ - { - "1280x1024-75", { - 134989, 1280, 1024, 16, 160, 408, 1688, 1, 4, 42, - 1066, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1600x1200-60" */ - { - "1600x1200-60", { - 155981, 1600, 1200, 32, 192, 448, 2048, 10, 18, 70, - 1270, 1600, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1600x1200-66" */ - { - "1600x1200-66", { - 171998, 1600, 1200, 40, 176, 480, 2080, 3, 6, 53, - 1253, 1600, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1600x1200-76" */ - { - "1600x1200-76", { - 197980, 1600, 1200, 40, 176, 480, 2080, 3, 8, 50, - 1250, 1600, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* ##### end of auto-generated mode */ - { - "\0",} -}; -/* more mandatory stuff (see skeletonfb.c + framebuffer driver HOWTO */ -static struct pm3fb_info fb_info[PM3_MAX_BOARD]; -static struct pm3fb_par current_par[PM3_MAX_BOARD]; -static int current_par_valid[PM3_MAX_BOARD]; -/* to allow explicit filtering of board */ -short bus[PM3_MAX_BOARD]; -short slot[PM3_MAX_BOARD]; -short func[PM3_MAX_BOARD]; -short disable[PM3_MAX_BOARD]; -short noaccel[PM3_MAX_BOARD]; -char fontn[PM3_MAX_BOARD][PM3_FONTNAME_SIZE]; -short depth[PM3_MAX_BOARD]; -short flatpanel[PM3_MAX_BOARD]; -static struct display disp[PM3_MAX_BOARD]; -static char g_options[PM3_OPTIONS_SIZE] __initdata = "pm3fb,dummy"; -short printtimings = 0; -short forcesize[PM3_MAX_BOARD]; - -/* ********************* */ -/* ***** prototype ***** */ -/* ********************* */ -/* card-specific */ -static void pm3fb_j2000_setup(struct pm3fb_info *l_fb_info); -/* permedia3-specific */ -static pm3fb_timing_result pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info); -static pm3fb_timing_result pm3fb_try_memory_timings(struct pm3fb_info *l_fb_info); -static void pm3fb_write_memory_timings(struct pm3fb_info *l_fb_info); -static unsigned long pm3fb_read_dac_reg(struct pm3fb_info *l_fb_info, - unsigned long r); -static unsigned long pm3fb_CalculateClock(struct pm3fb_info *l_fb_info, unsigned long reqclock, /* In kHz units */ - unsigned long refclock, /* In kHz units */ - unsigned char *prescale, /* ClkPreScale */ - unsigned char *feedback, /* ClkFeedBackScale */ - unsigned char *postscale - /* ClkPostScale */ ); -static void pm3fb_clear_memory(struct pm3fb_info *l_fb_info, u32 cc); -static void pm3fb_clear_colormap(struct pm3fb_info *l_fb_info, unsigned char r, unsigned char g, unsigned char b); -static void pm3fb_common_init(struct pm3fb_info *l_fb_info); -static int pm3fb_Shiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v); -static int pm3fb_Unshiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v); -static void pm3fb_mapIO(struct pm3fb_info *l_fb_info); -static void pm3fb_unmapIO(struct pm3fb_info *l_fb_info); -#if defined(PM3FB_MASTER_DEBUG) && (PM3FB_MASTER_DEBUG >= 2) -static void pm3fb_show_cur_mode(struct pm3fb_info *l_fb_info); -#endif -static void pm3fb_show_cur_timing(struct pm3fb_info *l_fb_info); -static void pm3fb_write_mode(struct pm3fb_info *l_fb_info); -static void pm3fb_read_mode(struct pm3fb_info *l_fb_info, - struct pm3fb_par *curpar); -static unsigned long pm3fb_size_memory(struct pm3fb_info *l_fb_info); -/* accelerated permedia3-specific */ -#ifdef PM3FB_USE_ACCEL -static void pm3fb_wait_pm3(struct pm3fb_info *l_fb_info); -static void pm3fb_init_engine(struct pm3fb_info *l_fb_info); -#ifdef FBCON_HAS_CFB32 -static void pm3fb_cfb32_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width); -static void pm3fb_cfb32_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only); -#endif /* FBCON_HAS_CFB32 */ -#ifdef FBCON_HAS_CFB16 -static void pm3fb_cfb16_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width); -static void pm3fb_cfb16_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only); -#endif /* FBCON_HAS_CFB16 */ -#ifdef FBCON_HAS_CFB8 -static void pm3fb_cfb8_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width); -static void pm3fb_cfb8_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only); -#endif /* FBCON_HAS_CFB8 */ -#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB32) -static void pm3fb_cfbX_bmove(struct display *p, - int sy, int sx, - int dy, int dx, int height, int width); -static void pm3fb_cfbX_putc(struct vc_data *conp, struct display *p, - int c, int yy, int xx); -static void pm3fb_cfbX_putcs(struct vc_data *conp, struct display *p, - const unsigned short *s, int count, int yy, - int xx); -static void pm3fb_cfbX_revc(struct display *p, int xx, int yy); -#endif /* FBCON_HAS_CFB8 || FBCON_HAS_CFB16 || FBCON_HAS_CFB32 */ -#endif /* PM3FB_USE_ACCEL */ -/* pre-init */ -static void pm3fb_mode_setup(char *mode, unsigned long board_num); -static void pm3fb_pciid_setup(char *pciid, unsigned long board_num); -static char *pm3fb_boardnum_setup(char *options, unsigned long *bn); -static void pm3fb_real_setup(char *options); -/* fbdev */ -static int pm3fb_encode_fix(struct fb_fix_screeninfo *fix, - const void *par, struct fb_info_gen *info); -static int pm3fb_decode_var(const struct fb_var_screeninfo *var, - void *par, struct fb_info_gen *info); -static void pm3fb_encode_depth(struct fb_var_screeninfo *var, long d); -static int pm3fb_encode_var(struct fb_var_screeninfo *var, - const void *par, struct fb_info_gen *info); -static void pm3fb_get_par(void *par, struct fb_info_gen *info); -static void pm3fb_set_par(const void *par, struct fb_info_gen *info); -static void pm3fb_set_color(struct pm3fb_info *l_fb_info, - unsigned char regno, unsigned char r, - unsigned char g, unsigned char b); -static int pm3fb_getcolreg(unsigned regno, unsigned *red, unsigned *green, - unsigned *blue, unsigned *transp, - struct fb_info *info); -static int pm3fb_setcolreg(unsigned regno, unsigned red, unsigned green, - unsigned blue, unsigned transp, - struct fb_info *info); -static int pm3fb_blank(int blank_mode, struct fb_info_gen *info); -static void pm3fb_set_disp(const void *par, struct display *disp, - struct fb_info_gen *info); -static void pm3fb_detect(void); -static int pm3fb_pan_display(const struct fb_var_screeninfo *var, - struct fb_info_gen *info); -static int pm3fb_ioctl(struct fb_info *info, u_int cmd, u_long arg); - - -/* the struct that hold them together */ -struct fbgen_hwswitch pm3fb_switch = { - pm3fb_detect, pm3fb_encode_fix, pm3fb_decode_var, pm3fb_encode_var, - pm3fb_get_par, pm3fb_set_par, pm3fb_getcolreg, - pm3fb_pan_display, pm3fb_blank, pm3fb_set_disp -}; +/* + * Driver data + */ +static char *mode_option __devinitdata; -static struct fb_ops pm3fb_ops = { - .owner = THIS_MODULE, - .fb_get_fix = fbgen_get_fix, - .fb_get_var = fbgen_get_var, - .fb_set_var = fbgen_set_var, - .fb_get_cmap = fbgen_get_cmap, - .fb_set_cmap = fbgen_set_cmap, - .fb_setcolreg = pm3fb_setcolreg, - .fb_pan_display =fbgen_pan_display, - .fb_blank = fbgen_blank, - .fb_ioctl = pm3fb_ioctl, -}; +/* + * If your driver supports multiple boards, you should make the + * below data types arrays, or allocate them dynamically (using kmalloc()). + */ -#ifdef PM3FB_USE_ACCEL -#ifdef FBCON_HAS_CFB32 -static struct display_switch pm3fb_cfb32 = { - fbcon_cfb32_setup, pm3fb_cfbX_bmove, pm3fb_cfb32_clear, - pm3fb_cfbX_putc, pm3fb_cfbX_putcs, pm3fb_cfbX_revc, - NULL /* cursor() */ , NULL /* set_font() */ , - pm3fb_cfb32_clear_margins, - FONTWIDTHRANGE(1, 16) /* true only if accelerated... */ -}; -#endif /* FBCON_HAS_CFB32 */ -#ifdef FBCON_HAS_CFB16 -static struct display_switch pm3fb_cfb16 = { - fbcon_cfb16_setup, pm3fb_cfbX_bmove, pm3fb_cfb16_clear, - pm3fb_cfbX_putc, pm3fb_cfbX_putcs, pm3fb_cfbX_revc, - NULL /* cursor() */ , NULL /* set_font() */ , - pm3fb_cfb16_clear_margins, - FONTWIDTHRANGE(1, 16) /* true only if accelerated... */ -}; -#endif /* FBCON_HAS_CFB16 */ -#ifdef FBCON_HAS_CFB8 -static struct display_switch pm3fb_cfb8 = { - fbcon_cfb8_setup, pm3fb_cfbX_bmove, pm3fb_cfb8_clear, - pm3fb_cfbX_putc, pm3fb_cfbX_putcs, pm3fb_cfbX_revc, - NULL /* cursor() */ , NULL /* set_font() */ , - pm3fb_cfb8_clear_margins, - FONTWIDTHRANGE(1, 16) /* true only if accelerated... */ -}; -#endif /* FBCON_HAS_CFB8 */ -#endif /* PM3FB_USE_ACCEL */ - -/* ****************************** */ -/* ***** card-specific data ***** */ -/* ****************************** */ -struct pm3fb_card_timings { - unsigned long memsize; /* 0 for last value (i.e. default) */ - struct pm3fb_timings memt; +/* + * This structure defines the hardware state of the graphics card. Normally + * you place this in a header file in linux/include/video. This file usually + * also includes register information. That allows other driver subsystems + * and userland applications the ability to use the same header file to + * avoid duplicate work and easy porting of software. + */ +struct pm3_par { + unsigned char __iomem *v_regs;/* virtual address of p_regs */ + u32 video; /* video flags before blanking */ + u32 base; /* screen base (xoffset+yoffset) in 128 bits unit */ + u32 palette[16]; }; -static struct pm3fb_card_timings t_FormacProFormance3[] = { - { 16, { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} }, - { 0, { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} } /* from 16 MB PF3 */ +/* + * Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo + * if we don't use modedb. If we do use modedb see pm3fb_init how to use it + * to get a fb_var_screeninfo. Otherwise define a default var as well. + */ +static struct fb_fix_screeninfo pm3fb_fix __devinitdata = { + .id = "Permedia3", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_PSEUDOCOLOR, + .xpanstep = 1, + .ypanstep = 1, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, }; -static struct pm3fb_card_timings t_AppianJeronimo2000[] = { - { 32, { 0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} }, - { 0, { 0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} } /* from 32MB J2000 */ -}; +/* + * Utility functions + */ -static struct pm3fb_card_timings t_3DLabsOxygenVX1[] = { - { 32, { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000} }, - { 0, { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000} } /* from 32MB VX1 */ -}; +static inline u32 PM3_READ_REG(struct pm3_par *par, s32 off) +{ + return fb_readl(par->v_regs + off); +} -static struct { - char cardname[32]; /* recognized card name */ - u16 subvendor; /* subvendor of the card */ - u16 subdevice; /* subdevice of the card */ - u8 func; /* function of the card to which the extra init apply */ - void (*specific_setup)(struct pm3fb_info *l_fb_info); /* card/func specific setup, done before _any_ FB access */ - struct pm3fb_card_timings *c_memt; /* defauls timings for the boards */ -} cardbase[] = { - { "Unknown Permedia3 board", 0xFFFF, 0xFFFF, 0xFF, NULL, NULL }, - { "Appian Jeronimo 2000 head 1", 0x1097, 0x3d32, 1, NULL, - t_AppianJeronimo2000 - }, - { "Appian Jeronimo 2000 head 2", 0x1097, 0x3d32, 2, pm3fb_j2000_setup, - t_AppianJeronimo2000 - }, - { "Formac ProFormance 3", PCI_VENDOR_ID_3DLABS, 0x000a, 0, NULL, /* Formac use 3DLabs ID ?!? */ - t_FormacProFormance3 - }, - { "3DLabs Permedia3 Create!", PCI_VENDOR_ID_3DLABS, 0x0127, 0, NULL, NULL }, - { "3DLabs Oxygen VX1 PCI", PCI_VENDOR_ID_3DLABS, 0x0121, 0, NULL, - t_3DLabsOxygenVX1 - }, - { "3DLabs Oxygen VX1 AGP", PCI_VENDOR_ID_3DLABS, 0x0125, 0, NULL, NULL }, - { "3DLabs Oxygen VX1-16 AGP", PCI_VENDOR_ID_3DLABS, 0x0140, 0, NULL, NULL }, - { "3DLabs Oxygen VX1-1600SW PCI", PCI_VENDOR_ID_3DLABS, 0x0800, 0, NULL, NULL }, - { "\0", 0x0, 0x0, 0, NULL, NULL } -}; +static inline void PM3_WRITE_REG(struct pm3_par *par, s32 off, u32 v) +{ + fb_writel(v, par->v_regs + off); +} -/* ********************************** */ -/* ***** card-specific function ***** */ -/* ********************************** */ -static void pm3fb_j2000_setup(struct pm3fb_info *l_fb_info) -{ /* the appian j2000 require more initialization of the second head */ - /* l_fb_info must point to the _second_ head of the J2000 */ - - DTRACE; - - l_fb_info->memt = t_AppianJeronimo2000[0].memt; /* 32 MB, first and only j2000 ? */ - - pm3fb_write_memory_timings(l_fb_info); +static inline void PM3_WAIT(struct pm3_par *par, u32 n) +{ + while (PM3_READ_REG(par, PM3InFIFOSpace) < n); } -/* *************************************** */ -/* ***** permedia3-specific function ***** */ -/* *************************************** */ -static pm3fb_timing_result pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info) +static inline void PM3_SLOW_WRITE_REG(struct pm3_par *par, s32 off, u32 v) { - l_fb_info->memt.caps = PM3_READ_REG(PM3LocalMemCaps); - l_fb_info->memt.timings = PM3_READ_REG(PM3LocalMemTimings); - l_fb_info->memt.control = PM3_READ_REG(PM3LocalMemControl); - l_fb_info->memt.refresh = PM3_READ_REG(PM3LocalMemRefresh); - l_fb_info->memt.powerdown = PM3_READ_REG(PM3LocalMemPowerDown); - - if ((l_fb_info->memt.caps == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.timings == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.control == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.refresh == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.powerdown == PM3FB_UNKNOWN_TIMING_VALUE)) - { - printk(KERN_ERR "pm3fb: invalid memory timings in permedia3 board #%ld\n", l_fb_info->board_num); - return(pm3fb_try_memory_timings(l_fb_info)); + if (par->v_regs) { + mb(); + PM3_WAIT(par, 1); + wmb(); + PM3_WRITE_REG(par, off, v); } - return(pm3fb_timing_ok); } -static pm3fb_timing_result pm3fb_try_memory_timings(struct pm3fb_info *l_fb_info) +static inline void PM3_SET_INDEX(struct pm3_par *par, unsigned index) { - if (cardbase[l_fb_info->board_type].c_memt) - { - int i = 0, done = 0; - while (!done) - { - if ((cardbase[l_fb_info->board_type].c_memt[i].memsize == l_fb_info->fb_size) - || !(cardbase[l_fb_info->board_type].c_memt[i].memsize)) - { /* will use the 0-sized timings by default */ - done = 1; - l_fb_info->memt = cardbase[l_fb_info->board_type].c_memt[i].memt; - printk(KERN_WARNING "pm3fb: trying to use predefined memory timings for permedia3 board #%ld (%s, %ld MB)\n", - l_fb_info->board_num, - cardbase[l_fb_info->board_type].cardname, - cardbase[l_fb_info->board_type].c_memt[i].memsize); - pm3fb_write_memory_timings(l_fb_info); - return(pm3fb_timing_retry); - } - i++; - } - } else - return(pm3fb_timing_problem); - return(pm3fb_timing_ok); + PM3_SLOW_WRITE_REG(par, PM3RD_IndexHigh, (index >> 8) & 0xff); + PM3_SLOW_WRITE_REG(par, PM3RD_IndexLow, index & 0xff); } -static void pm3fb_write_memory_timings(struct pm3fb_info *l_fb_info) +static inline void PM3_WRITE_DAC_REG(struct pm3_par *par, unsigned r, u8 v) { - unsigned char m, n, p; - unsigned long clockused; - - PM3_SLOW_WRITE_REG(PM3LocalMemCaps, l_fb_info->memt.caps); - PM3_SLOW_WRITE_REG(PM3LocalMemTimings, l_fb_info->memt.timings); - PM3_SLOW_WRITE_REG(PM3LocalMemControl, l_fb_info->memt.control); - PM3_SLOW_WRITE_REG(PM3LocalMemRefresh, l_fb_info->memt.refresh); - PM3_SLOW_WRITE_REG(PM3LocalMemPowerDown, l_fb_info->memt.powerdown); - - clockused = - pm3fb_CalculateClock(l_fb_info, 2 * 105000, PM3_REF_CLOCK, &m, - &n, &p); - - PM3_WRITE_DAC_REG(PM3RD_KClkPreScale, m); - PM3_WRITE_DAC_REG(PM3RD_KClkFeedbackScale, n); - PM3_WRITE_DAC_REG(PM3RD_KClkPostScale, p); - PM3_WRITE_DAC_REG(PM3RD_KClkControl, - PM3RD_KClkControl_STATE_RUN | - PM3RD_KClkControl_SOURCE_PLL | - PM3RD_KClkControl_ENABLE); - PM3_WRITE_DAC_REG(PM3RD_MClkControl, - PM3RD_MClkControl_STATE_RUN | - PM3RD_MClkControl_SOURCE_KCLK | - PM3RD_MClkControl_ENABLE); - PM3_WRITE_DAC_REG(PM3RD_SClkControl, - PM3RD_SClkControl_STATE_RUN | - PM3RD_SClkControl_SOURCE_PCLK | - PM3RD_SClkControl_ENABLE); + PM3_SET_INDEX(par, r); + wmb(); + PM3_WRITE_REG(par, PM3RD_IndexedData, v); } -static unsigned long pm3fb_read_dac_reg(struct pm3fb_info *l_fb_info, - unsigned long r) +static inline void pm3fb_set_color(struct pm3_par *par, unsigned char regno, + unsigned char r, unsigned char g, unsigned char b) { - DASSERT((l_fb_info->vIOBase != (unsigned char *) (-1)), - "l_fb_info->vIOBase mapped in read dac reg\n"); - PM3_SET_INDEX(r); - mb(); - return (PM3_READ_REG(PM3RD_IndexedData)); + PM3_SLOW_WRITE_REG(par, PM3RD_PaletteWriteAddress, regno); + PM3_SLOW_WRITE_REG(par, PM3RD_PaletteData, r); + PM3_SLOW_WRITE_REG(par, PM3RD_PaletteData, g); + PM3_SLOW_WRITE_REG(par, PM3RD_PaletteData, b); +} + +static void pm3fb_clear_colormap(struct pm3_par *par, + unsigned char r, unsigned char g, unsigned char b) +{ + int i; + + for (i = 0; i < 256 ; i++) /* fill color map with white */ + pm3fb_set_color(par, i, r, g, b); + } /* Calculating various clock parameter */ -static unsigned long pm3fb_CalculateClock(struct pm3fb_info *l_fb_info, unsigned long reqclock, /* In kHz units */ - unsigned long refclock, /* In kHz units */ - unsigned char *prescale, /* ClkPreScale */ - unsigned char *feedback, /* ClkFeedBackScale */ - unsigned char *postscale - /* ClkPostScale */ ) +static void pm3fb_calculate_clock(unsigned long reqclock, + unsigned char *prescale, + unsigned char *feedback, + unsigned char *postscale) { int f, pre, post; unsigned long freq; long freqerr = 1000; - unsigned long actualclock = 0; - - DTRACE; + long currerr; for (f = 1; f < 256; f++) { for (pre = 1; pre < 256; pre++) { for (post = 0; post < 5; post++) { - freq = - ((2 * refclock * f) / - (pre * (1 << post))); - if ((reqclock > freq - freqerr) - && (reqclock < freq + freqerr)) { - freqerr = - (reqclock > - freq) ? reqclock - - freq : freq - reqclock; + freq = ((2*PM3_REF_CLOCK * f) >> post) / pre; + currerr = (reqclock > freq) + ? reqclock - freq + : freq - reqclock; + if (currerr < freqerr) { + freqerr = currerr; *feedback = f; *prescale = pre; *postscale = post; - actualclock = freq; } } } } - - return (actualclock); } -static int pm3fb_Shiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v) +static inline int pm3fb_shift_bpp(unsigned long depth, int v) { - DTRACE; - switch (depth) { case 8: return (v >> 4); @@ -918,181 +187,59 @@ static int pm3fb_Shiftbpp(struct pm3fb_info *l_fb_info, case 32: return (v >> 2); } - DPRINTK(1, "Unsupported depth %ld\n", depth); - return (0); -} - -static int pm3fb_Unshiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v) -{ - DTRACE; - - switch (depth) { - case 8: - return (v << 4); - case 12: - case 15: - case 16: - return (v << 3); - case 32: - return (v << 2); - } - DPRINTK(1, "Unsupported depth %ld\n", depth); - return (0); -} - -static void pm3fb_mapIO(struct pm3fb_info *l_fb_info) -{ - DTRACE; - - l_fb_info->vIOBase = - ioremap((unsigned long) l_fb_info->pIOBase, PM3_REGS_SIZE); - l_fb_info->v_fb = - ioremap((unsigned long) l_fb_info->p_fb, l_fb_info->fb_size); - DPRINTK(2, "IO mapping : IOBase %lx / %lx, fb %lx / %lx\n", - (unsigned long) l_fb_info->pIOBase, - (unsigned long) l_fb_info->vIOBase, - (unsigned long) l_fb_info->p_fb, - (unsigned long) l_fb_info->v_fb); -} - -static void pm3fb_unmapIO(struct pm3fb_info *l_fb_info) -{ - DTRACE; - - iounmap(l_fb_info->vIOBase); - iounmap(l_fb_info->v_fb); - l_fb_info->vIOBase = (unsigned char *) -1; - l_fb_info->v_fb = (unsigned char *) -1; -} - -#if defined(PM3FB_MASTER_DEBUG) && (PM3FB_MASTER_DEBUG >= 2) -static void pm3fb_show_cur_mode(struct pm3fb_info *l_fb_info) -{ - DPRINTK(2, "PM3Aperture0: 0x%08x\n", PM3_READ_REG(PM3Aperture0)); - DPRINTK(2, "PM3Aperture1: 0x%08x\n", PM3_READ_REG(PM3Aperture1)); - DPRINTK(2, "PM3ByAperture1Mode: 0x%08x\n", - PM3_READ_REG(PM3ByAperture1Mode)); - DPRINTK(2, "PM3ByAperture2Mode: 0x%08x\n", - PM3_READ_REG(PM3ByAperture2Mode)); - DPRINTK(2, "PM3ChipConfig: 0x%08x\n", PM3_READ_REG(PM3ChipConfig)); - DPRINTK(2, "PM3FIFODis: 0x%08x\n", PM3_READ_REG(PM3FIFODis)); - DPRINTK(2, "PM3HTotal: 0x%08x\n", PM3_READ_REG(PM3HTotal)); - DPRINTK(2, "PM3HbEnd: 0x%08x\n", PM3_READ_REG(PM3HbEnd)); - DPRINTK(2, "PM3HgEnd: 0x%08x\n", PM3_READ_REG(PM3HgEnd)); - DPRINTK(2, "PM3HsEnd: 0x%08x\n", PM3_READ_REG(PM3HsEnd)); - DPRINTK(2, "PM3HsStart: 0x%08x\n", PM3_READ_REG(PM3HsStart)); - DPRINTK(2, "PM3MemBypassWriteMask: 0x%08x\n", - PM3_READ_REG(PM3MemBypassWriteMask)); - DPRINTK(2, "PM3RD_IndexControl: 0x%08x\n", - PM3_READ_REG(PM3RD_IndexControl)); - DPRINTK(2, "PM3ScreenBase: 0x%08x\n", PM3_READ_REG(PM3ScreenBase)); - DPRINTK(2, "PM3ScreenStride: 0x%08x\n", - PM3_READ_REG(PM3ScreenStride)); - DPRINTK(2, "PM3VClkCtl: 0x%08x\n", PM3_READ_REG(PM3VClkCtl)); - DPRINTK(2, "PM3VTotal: 0x%08x\n", PM3_READ_REG(PM3VTotal)); - DPRINTK(2, "PM3VbEnd: 0x%08x\n", PM3_READ_REG(PM3VbEnd)); - DPRINTK(2, "PM3VideoControl: 0x%08x\n", - PM3_READ_REG(PM3VideoControl)); - DPRINTK(2, "PM3VsEnd: 0x%08x\n", PM3_READ_REG(PM3VsEnd)); - DPRINTK(2, "PM3VsStart: 0x%08x\n", PM3_READ_REG(PM3VsStart)); - - DPRINTK(2, "PM3RD_ColorFormat: %ld\n", - PM3_READ_DAC_REG(PM3RD_ColorFormat)); - DPRINTK(2, "PM3RD_DACControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_DACControl)); - DPRINTK(2, "PM3RD_DClk0FeedbackScale: %ld\n", - PM3_READ_DAC_REG(PM3RD_DClk0FeedbackScale)); - DPRINTK(2, "PM3RD_DClk0PostScale: %ld\n", - PM3_READ_DAC_REG(PM3RD_DClk0PostScale)); - DPRINTK(2, "PM3RD_DClk0PreScale: %ld\n", - PM3_READ_DAC_REG(PM3RD_DClk0PreScale)); - DPRINTK(2, "[not set] PM3RD_IndexControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_IndexControl)); - DPRINTK(2, "PM3RD_MiscControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_MiscControl)); - DPRINTK(2, "PM3RD_PixelSize: %ld\n", - PM3_READ_DAC_REG(PM3RD_PixelSize)); - DPRINTK(2, "PM3RD_SyncControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_SyncControl)); -} - -#endif /* defined(PM3FB_MASTER_DEBUG) && (PM3FB_MASTER_DEBUG >= 2) */ -static void pm3fb_show_cur_timing(struct pm3fb_info *l_fb_info) -{ - u16 subvendor, subdevice; - - if ((!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor)) - && - (!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_ID, &subdevice))) { - /* well, nothing... */ - } else { - subvendor = subdevice = (u16)-1; - } - - printk(KERN_INFO "pm3fb: memory timings for board #%ld (subvendor: 0x%hx, subdevice: 0x%hx)\n", l_fb_info->board_num, subvendor, subdevice); - printk(KERN_INFO " PM3LocalMemCaps: 0x%08x\n", - PM3_READ_REG(PM3LocalMemCaps)); - printk(KERN_INFO " PM3LocalMemTimings: 0x%08x\n", - PM3_READ_REG(PM3LocalMemTimings)); - printk(KERN_INFO " PM3LocalMemControl: 0x%08x\n", - PM3_READ_REG(PM3LocalMemControl)); - printk(KERN_INFO " PM3LocalMemRefresh: 0x%08x\n", - PM3_READ_REG(PM3LocalMemRefresh)); - printk(KERN_INFO " PM3LocalMemPowerDown: 0x%08x\n", - PM3_READ_REG(PM3LocalMemPowerDown)); + DPRINTK("Unsupported depth %ld\n", depth); + return 0; } /* write the mode to registers */ -static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) +static void pm3fb_write_mode(struct fb_info *info) { + struct pm3_par *par = info->par; char tempsync = 0x00, tempmisc = 0x00; - DTRACE; - - PM3_SLOW_WRITE_REG(PM3MemBypassWriteMask, 0xffffffff); - PM3_SLOW_WRITE_REG(PM3Aperture0, 0x00000000); - PM3_SLOW_WRITE_REG(PM3Aperture1, 0x00000000); - PM3_SLOW_WRITE_REG(PM3FIFODis, 0x00000007); - - PM3_SLOW_WRITE_REG(PM3HTotal, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->htotal - - 1)); - PM3_SLOW_WRITE_REG(PM3HsEnd, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->hsend)); - PM3_SLOW_WRITE_REG(PM3HsStart, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par-> + const u32 hsstart = info->var.right_margin; + const u32 hsend = hsstart + info->var.hsync_len; + const u32 hbend = hsend + info->var.left_margin; + const u32 xres = (info->var.xres + 31) & ~31; + const u32 htotal = xres + hbend; + const u32 vsstart = info->var.lower_margin; + const u32 vsend = vsstart + info->var.vsync_len; + const u32 vbend = vsend + info->var.upper_margin; + const u32 vtotal = info->var.yres + vbend; + const u32 width = (info->var.xres_virtual + 7) & ~7; + + PM3_SLOW_WRITE_REG(par, PM3MemBypassWriteMask, 0xffffffff); + PM3_SLOW_WRITE_REG(par, PM3Aperture0, 0x00000000); + PM3_SLOW_WRITE_REG(par, PM3Aperture1, 0x00000000); + PM3_SLOW_WRITE_REG(par, PM3FIFODis, 0x00000007); + + PM3_SLOW_WRITE_REG(par, PM3HTotal, + pm3fb_shift_bpp(info->var.bits_per_pixel, + htotal - 1)); + PM3_SLOW_WRITE_REG(par, PM3HsEnd, + pm3fb_shift_bpp(info->var.bits_per_pixel, + hsend)); + PM3_SLOW_WRITE_REG(par, PM3HsStart, + pm3fb_shift_bpp(info->var.bits_per_pixel, hsstart)); - PM3_SLOW_WRITE_REG(PM3HbEnd, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->hbend)); - PM3_SLOW_WRITE_REG(PM3HgEnd, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->hbend)); - PM3_SLOW_WRITE_REG(PM3ScreenStride, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->stride)); - PM3_SLOW_WRITE_REG(PM3VTotal, l_fb_info->current_par->vtotal - 1); - PM3_SLOW_WRITE_REG(PM3VsEnd, l_fb_info->current_par->vsend - 1); - PM3_SLOW_WRITE_REG(PM3VsStart, - l_fb_info->current_par->vsstart - 1); - PM3_SLOW_WRITE_REG(PM3VbEnd, l_fb_info->current_par->vbend); - - switch (l_fb_info->current_par->depth) { + PM3_SLOW_WRITE_REG(par, PM3HbEnd, + pm3fb_shift_bpp(info->var.bits_per_pixel, + hbend)); + PM3_SLOW_WRITE_REG(par, PM3HgEnd, + pm3fb_shift_bpp(info->var.bits_per_pixel, + hbend)); + PM3_SLOW_WRITE_REG(par, PM3ScreenStride, + pm3fb_shift_bpp(info->var.bits_per_pixel, + width)); + PM3_SLOW_WRITE_REG(par, PM3VTotal, vtotal - 1); + PM3_SLOW_WRITE_REG(par, PM3VsEnd, vsend - 1); + PM3_SLOW_WRITE_REG(par, PM3VsStart, vsstart - 1); + PM3_SLOW_WRITE_REG(par, PM3VbEnd, vbend); + + switch (info->var.bits_per_pixel) { case 8: - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_8BIT); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_8BIT); break; @@ -1100,15 +247,15 @@ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) case 15: case 16: #ifndef __BIG_ENDIAN - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_16BIT); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_16BIT); #else - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_16BIT | PM3ByApertureMode_BYTESWAP_BADC); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_16BIT | PM3ByApertureMode_BYTESWAP_BADC); #endif /* ! __BIG_ENDIAN */ @@ -1116,23 +263,23 @@ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) case 32: #ifndef __BIG_ENDIAN - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_32BIT); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_32BIT); #else - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_32BIT | PM3ByApertureMode_BYTESWAP_DCBA); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_32BIT | PM3ByApertureMode_BYTESWAP_DCBA); #endif /* ! __BIG_ENDIAN */ break; default: - DPRINTK(1, "Unsupported depth %d\n", - l_fb_info->current_par->depth); + DPRINTK("Unsupported depth %d\n", + info->var.bits_per_pixel); break; } @@ -1143,95 +290,86 @@ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) * sync options in PM3RD_SyncControl. --rmk */ { - unsigned int video = l_fb_info->current_par->video; + unsigned int video = par->video; video &= ~(PM3VideoControl_HSYNC_MASK | PM3VideoControl_VSYNC_MASK); video |= PM3VideoControl_HSYNC_ACTIVE_HIGH | PM3VideoControl_VSYNC_ACTIVE_HIGH; - PM3_SLOW_WRITE_REG(PM3VideoControl, video); + PM3_SLOW_WRITE_REG(par, PM3VideoControl, video); } - PM3_SLOW_WRITE_REG(PM3VClkCtl, - (PM3_READ_REG(PM3VClkCtl) & 0xFFFFFFFC)); - PM3_SLOW_WRITE_REG(PM3ScreenBase, l_fb_info->current_par->base); - PM3_SLOW_WRITE_REG(PM3ChipConfig, - (PM3_READ_REG(PM3ChipConfig) & 0xFFFFFFFD)); + PM3_SLOW_WRITE_REG(par, PM3VClkCtl, + (PM3_READ_REG(par, PM3VClkCtl) & 0xFFFFFFFC)); + PM3_SLOW_WRITE_REG(par, PM3ScreenBase, par->base); + PM3_SLOW_WRITE_REG(par, PM3ChipConfig, + (PM3_READ_REG(par, PM3ChipConfig) & 0xFFFFFFFD)); { - unsigned char m; /* ClkPreScale */ - unsigned char n; /* ClkFeedBackScale */ - unsigned char p; /* ClkPostScale */ - (void)pm3fb_CalculateClock(l_fb_info, l_fb_info->current_par->pixclock, PM3_REF_CLOCK, &m, &n, &p); - - DPRINTK(2, - "Pixclock: %d, Pre: %d, Feedback: %d, Post: %d\n", - l_fb_info->current_par->pixclock, (int) m, (int) n, - (int) p); - - PM3_WRITE_DAC_REG(PM3RD_DClk0PreScale, m); - PM3_WRITE_DAC_REG(PM3RD_DClk0FeedbackScale, n); - PM3_WRITE_DAC_REG(PM3RD_DClk0PostScale, p); + unsigned char uninitialized_var(m); /* ClkPreScale */ + unsigned char uninitialized_var(n); /* ClkFeedBackScale */ + unsigned char uninitialized_var(p); /* ClkPostScale */ + unsigned long pixclock = PICOS2KHZ(info->var.pixclock); + + (void)pm3fb_calculate_clock(pixclock, &m, &n, &p); + + DPRINTK("Pixclock: %ld, Pre: %d, Feedback: %d, Post: %d\n", + pixclock, (int) m, (int) n, (int) p); + + PM3_WRITE_DAC_REG(par, PM3RD_DClk0PreScale, m); + PM3_WRITE_DAC_REG(par, PM3RD_DClk0FeedbackScale, n); + PM3_WRITE_DAC_REG(par, PM3RD_DClk0PostScale, p); } /* - PM3_WRITE_DAC_REG(PM3RD_IndexControl, 0x00); + PM3_WRITE_DAC_REG(par, PM3RD_IndexControl, 0x00); */ /* - PM3_SLOW_WRITE_REG(PM3RD_IndexControl, 0x00); + PM3_SLOW_WRITE_REG(par, PM3RD_IndexControl, 0x00); */ - if ((l_fb_info->current_par->video & PM3VideoControl_HSYNC_MASK) == + if ((par->video & PM3VideoControl_HSYNC_MASK) == PM3VideoControl_HSYNC_ACTIVE_HIGH) tempsync |= PM3RD_SyncControl_HSYNC_ACTIVE_HIGH; - if ((l_fb_info->current_par->video & PM3VideoControl_VSYNC_MASK) == + if ((par->video & PM3VideoControl_VSYNC_MASK) == PM3VideoControl_VSYNC_ACTIVE_HIGH) tempsync |= PM3RD_SyncControl_VSYNC_ACTIVE_HIGH; - - PM3_WRITE_DAC_REG(PM3RD_SyncControl, tempsync); - DPRINTK(2, "PM3RD_SyncControl: %d\n", tempsync); - - if (flatpanel[l_fb_info->board_num]) - { - PM3_WRITE_DAC_REG(PM3RD_DACControl, PM3RD_DACControl_BLANK_PEDESTAL_ENABLE); - PM3_WAIT(2); - PM3_WRITE_REG(PM3VSConfiguration, 0x06); - PM3_WRITE_REG(0x5a00, 1 << 14); /* black magic... */ - tempmisc = PM3RD_MiscControl_VSB_OUTPUT_ENABLE; - } - else - PM3_WRITE_DAC_REG(PM3RD_DACControl, 0x00); - switch (l_fb_info->current_par->depth) { + PM3_WRITE_DAC_REG(par, PM3RD_SyncControl, tempsync); + DPRINTK("PM3RD_SyncControl: %d\n", tempsync); + + PM3_WRITE_DAC_REG(par, PM3RD_DACControl, 0x00); + + switch (info->var.bits_per_pixel) { case 8: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_8_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_CI8_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW); tempmisc |= PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; break; case 12: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_16_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_4444_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW | PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE); tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE | PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; - break; + break; case 15: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_16_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_5551_FRONT_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW | PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE); tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE | PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; - break; + break; case 16: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_16_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_565_FRONT_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW | PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE); @@ -1239,1936 +377,280 @@ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; break; case 32: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_32_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_8888_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW); tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE | PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; break; } - PM3_WRITE_DAC_REG(PM3RD_MiscControl, tempmisc); - - PM3_SHOW_CUR_MODE; + PM3_WRITE_DAC_REG(par, PM3RD_MiscControl, tempmisc); } -static void pm3fb_read_mode(struct pm3fb_info *l_fb_info, - struct pm3fb_par *curpar) -{ - unsigned long pixsize1, pixsize2, clockused; - unsigned long pre, feedback, post; - - DTRACE; - - clockused = PM3_READ_REG(PM3VClkCtl); +/* + * hardware independent functions + */ +int pm3fb_init(void); +int pm3fb_setup(char*); - switch (clockused) { - case 3: - pre = PM3_READ_DAC_REG(PM3RD_DClk3PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk3FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk3PostScale); +static int pm3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + u32 lpitch; - DPRINTK(2, - "DClk3 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); + var->transp.offset = 0; + var->transp.length = 0; + switch(var->bits_per_pixel) { + case 8: + var->red.length = var->green.length = var->blue.length = 8; + var->red.offset = var->green.offset = var->blue.offset = 0; break; - case 2: - pre = PM3_READ_DAC_REG(PM3RD_DClk2PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk2FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk2PostScale); - - DPRINTK(2, - "DClk2 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); + case 12: + var->red.offset = 8; + var->red.length = 4; + var->green.offset = 4; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + var->transp.offset = 12; + var->transp.length = 4; + case 15: + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 15; + var->transp.length = 1; break; - case 1: - pre = PM3_READ_DAC_REG(PM3RD_DClk1PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk1FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk1PostScale); - - DPRINTK(2, - "DClk1 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); + case 16: + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; break; - case 0: - pre = PM3_READ_DAC_REG(PM3RD_DClk0PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk0FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk0PostScale); - - DPRINTK(2, - "DClk0 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); + case 32: + var->transp.offset = 24; + var->transp.length = 8; + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length = 8; break; default: - pre = feedback = post = 0; - DPRINTK(1, "Unknowk D clock used : %ld\n", clockused); - break; - } - - curpar->pixclock = PM3_SCALE_TO_CLOCK(pre, feedback, post); - - pixsize1 = - PM3ByApertureMode_PIXELSIZE_MASK & - (PM3_READ_REG(PM3ByAperture1Mode)); - pixsize2 = - PM3ByApertureMode_PIXELSIZE_MASK & - (PM3_READ_REG(PM3ByAperture2Mode)); - - DASSERT((pixsize1 == pixsize2), - "pixsize the same in both aperture\n"); - - if (pixsize1 & PM3ByApertureMode_PIXELSIZE_32BIT) - curpar->depth = 32; - else if (pixsize1 & PM3ByApertureMode_PIXELSIZE_16BIT) - { - curpar->depth = 16; - } - else - curpar->depth = 8; - - /* not sure if I need to add one on the next ; it give better result with */ - curpar->htotal = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - 1 + PM3_READ_REG(PM3HTotal)); - curpar->hsend = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3HsEnd)); - curpar->hsstart = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3HsStart)); - curpar->hbend = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3HbEnd)); - - curpar->stride = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3ScreenStride)); - - curpar->vtotal = 1 + PM3_READ_REG(PM3VTotal); - curpar->vsend = 1 + PM3_READ_REG(PM3VsEnd); - curpar->vsstart = 1 + PM3_READ_REG(PM3VsStart); - curpar->vbend = PM3_READ_REG(PM3VbEnd); - - curpar->video = PM3_READ_REG(PM3VideoControl); - - curpar->base = PM3_READ_REG(PM3ScreenBase); - curpar->width = curpar->htotal - curpar->hbend; /* make virtual == displayed resolution */ - curpar->height = curpar->vtotal - curpar->vbend; - - DPRINTK(2, "Found : %d * %d, %d Khz, stride is %08x\n", - curpar->width, curpar->height, curpar->pixclock, - curpar->stride); -} - -static unsigned long pm3fb_size_memory(struct pm3fb_info *l_fb_info) -{ - unsigned long memsize = 0, tempBypass, i, temp1, temp2; - u16 subvendor, subdevice; - pm3fb_timing_result ptr; - - DTRACE; - - l_fb_info->fb_size = 64 * 1024 * 1024; /* pm3 aperture always 64 MB */ - pm3fb_mapIO(l_fb_info); /* temporary map IO */ - - DASSERT((l_fb_info->vIOBase != NULL), - "IO successfully mapped before mem detect\n"); - DASSERT((l_fb_info->v_fb != NULL), - "FB successfully mapped before mem detect\n"); - - /* card-specific stuff, *before* accessing *any* FB memory */ - if ((!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor)) - && - (!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_ID, &subdevice))) { - i = 0; l_fb_info->board_type = 0; - while ((cardbase[i].cardname[0]) && !(l_fb_info->board_type)) { - if ((cardbase[i].subvendor == subvendor) && - (cardbase[i].subdevice == subdevice) && - (cardbase[i].func == PCI_FUNC(l_fb_info->dev->devfn))) { - DPRINTK(2, "Card #%ld is an %s\n", - l_fb_info->board_num, - cardbase[i].cardname); - if (cardbase[i].specific_setup) - cardbase[i].specific_setup(l_fb_info); - l_fb_info->board_type = i; - } - i++; - } - if (!l_fb_info->board_type) { - DPRINTK(1, "Card #%ld is an unknown 0x%04x / 0x%04x\n", - l_fb_info->board_num, subvendor, subdevice); - } - } else { - printk(KERN_ERR "pm3fb: Error: pci_read_config_word failed, board #%ld\n", - l_fb_info->board_num); - } - - if (printtimings) - pm3fb_show_cur_timing(l_fb_info); - - /* card-specific setup is done, we preserve the final - memory timing for future reference */ - if ((ptr = pm3fb_preserve_memory_timings(l_fb_info)) == pm3fb_timing_problem) { /* memory timings were wrong ! oops.... */ - return(0); - } - - tempBypass = PM3_READ_REG(PM3MemBypassWriteMask); - - DPRINTK(2, "PM3MemBypassWriteMask was: 0x%08lx\n", tempBypass); - - PM3_SLOW_WRITE_REG(PM3MemBypassWriteMask, 0xFFFFFFFF); - - /* pm3 split up memory, replicates, and do a lot of nasty stuff IMHO ;-) */ - for (i = 0; i < 32; i++) { - fb_writel(i * 0x00345678, - (l_fb_info->v_fb + (i * 1048576))); - mb(); - temp1 = fb_readl((l_fb_info->v_fb + (i * 1048576))); - - /* Let's check for wrapover, write will fail at 16MB boundary */ - if (temp1 == (i * 0x00345678)) - memsize = i; - else - break; - } - - DPRINTK(2, "First detect pass already got %ld MB\n", memsize + 1); - - if (memsize == i) { - for (i = 0; i < 32; i++) { - /* Clear first 32MB ; 0 is 0, no need to byteswap */ - writel(0x0000000, - (l_fb_info->v_fb + (i * 1048576))); - mb(); - } - - for (i = 32; i < 64; i++) { - fb_writel(i * 0x00345678, - (l_fb_info->v_fb + (i * 1048576))); - mb(); - temp1 = - fb_readl((l_fb_info->v_fb + (i * 1048576))); - temp2 = - fb_readl((l_fb_info->v_fb + - ((i - 32) * 1048576))); - if ((temp1 == (i * 0x00345678)) && (temp2 == 0)) /* different value, different RAM... */ - memsize = i; - else - break; - } - } - - DPRINTK(2, "Second detect pass got %ld MB\n", memsize + 1); - - PM3_SLOW_WRITE_REG(PM3MemBypassWriteMask, tempBypass); - - pm3fb_unmapIO(l_fb_info); - memsize = 1048576 * (memsize + 1); - - DPRINTK(2, "Returning 0x%08lx bytes\n", memsize); - - if (forcesize[l_fb_info->board_num] && ((forcesize[l_fb_info->board_num] * 1048576) != memsize)) - { - printk(KERN_WARNING "pm3fb: mismatch between probed (%ld MB) and specified (%hd MB) memory size, using SPECIFIED !\n", memsize, forcesize[l_fb_info->board_num]); - memsize = 1048576 * forcesize[l_fb_info->board_num]; - } - - l_fb_info->fb_size = memsize; - - if (ptr == pm3fb_timing_retry) - { - printk(KERN_WARNING "pm3fb: retrying memory timings check"); - if (pm3fb_try_memory_timings(l_fb_info) == pm3fb_timing_problem) - return(0); - } - - return (memsize); -} - -static void pm3fb_clear_memory(struct pm3fb_info *l_fb_info, u32 cc) -{ - int i; - - DTRACE; - - for (i = 0; i < (l_fb_info->fb_size / sizeof(u32)) ; i++) /* clear entire FB memory to black */ - { - fb_writel(cc, (l_fb_info->v_fb + (i * sizeof(u32)))); + DPRINTK("depth not supported: %u\n", var->bits_per_pixel); + return -EINVAL; } -} - -static void pm3fb_clear_colormap(struct pm3fb_info *l_fb_info, unsigned char r, unsigned char g, unsigned char b) -{ - int i; - - DTRACE; - - for (i = 0; i < 256 ; i++) /* fill color map with white */ - pm3fb_set_color(l_fb_info, i, r, g, b); - -} - -/* common initialisation */ -static void pm3fb_common_init(struct pm3fb_info *l_fb_info) -{ - DTRACE; - - DPRINTK(2, "Initializing board #%ld @ %lx\n", l_fb_info->board_num, - (unsigned long) l_fb_info); - - strcpy(l_fb_info->gen.info.modename, permedia3_name); - disp[l_fb_info->board_num].scrollmode = 0; /* SCROLL_YNOMOVE; *//* 0 means "let fbcon choose" */ - l_fb_info->gen.parsize = sizeof(struct pm3fb_par); - l_fb_info->gen.info.changevar = NULL; - l_fb_info->gen.info.fbops = &pm3fb_ops; - l_fb_info->gen.info.disp = &(disp[l_fb_info->board_num]); - if (fontn[l_fb_info->board_num][0]) - strcpy(l_fb_info->gen.info.fontname, - fontn[l_fb_info->board_num]); - l_fb_info->gen.info.switch_con = &fbgen_switch; - l_fb_info->gen.info.updatevar = &fbgen_update_var; /* */ - l_fb_info->gen.info.flags = FBINFO_FLAG_DEFAULT; - - pm3fb_mapIO(l_fb_info); - - pm3fb_clear_memory(l_fb_info, 0); - pm3fb_clear_colormap(l_fb_info, 0, 0, 0); - - (void) fbgen_get_var(&(disp[l_fb_info->board_num]).var, -1, - &l_fb_info->gen.info); + var->height = var->width = -1; - if (depth[l_fb_info->board_num]) /* override mode-defined depth */ - { - pm3fb_encode_depth(&(disp[l_fb_info->board_num]).var, depth[l_fb_info->board_num]); - (disp[l_fb_info->board_num]).var.bits_per_pixel = depth2bpp(depth[l_fb_info->board_num]); + if (var->xres != var->xres_virtual) { + DPRINTK("virtual x resolution != physical x resolution not supported\n"); + return -EINVAL; } - (void) fbgen_do_set_var(&(disp[l_fb_info->board_num]).var, 1, - &l_fb_info->gen); - - fbgen_set_disp(-1, &l_fb_info->gen); - - do_install_cmap(0, &l_fb_info->gen.info); - - if (register_framebuffer(&l_fb_info->gen.info) < 0) { - DPRINTK(1, "Couldn't register framebuffer\n"); - return; + if (var->yres > var->yres_virtual) { + DPRINTK("virtual y resolution < physical y resolution not possible\n"); + return -EINVAL; } - PM3_WRITE_DAC_REG(PM3RD_CursorMode, - PM3RD_CursorMode_CURSOR_DISABLE); - - PM3_SHOW_CUR_MODE; - - pm3fb_write_mode(l_fb_info); - - printk("fb%d: %s, using %uK of video memory (%s)\n", - l_fb_info->gen.info.node, - permedia3_name, (u32) (l_fb_info->fb_size >> 10), - cardbase[l_fb_info->board_type].cardname); -} - -/* **************************************************** */ -/* ***** accelerated permedia3-specific functions ***** */ -/* **************************************************** */ -#ifdef PM3FB_USE_ACCEL -static void pm3fb_wait_pm3(struct pm3fb_info *l_fb_info) -{ - DTRACE; - - PM3_SLOW_WRITE_REG(PM3FilterMode, PM3FilterModeSync); - PM3_SLOW_WRITE_REG(PM3Sync, 0); - mb(); - do { - while ((PM3_READ_REG(PM3OutFIFOWords)) == 0); - rmb(); - } while ((PM3_READ_REG(PM3OutputFifo)) != PM3Sync_Tag); -} - -static void pm3fb_init_engine(struct pm3fb_info *l_fb_info) -{ - PM3_SLOW_WRITE_REG(PM3FilterMode, PM3FilterModeSync); - PM3_SLOW_WRITE_REG(PM3StatisticMode, 0x0); - PM3_SLOW_WRITE_REG(PM3DeltaMode, 0x0); - PM3_SLOW_WRITE_REG(PM3RasterizerMode, 0x0); - PM3_SLOW_WRITE_REG(PM3ScissorMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LineStippleMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AreaStippleMode, 0x0); - PM3_SLOW_WRITE_REG(PM3GIDMode, 0x0); - PM3_SLOW_WRITE_REG(PM3DepthMode, 0x0); - PM3_SLOW_WRITE_REG(PM3StencilMode, 0x0); - PM3_SLOW_WRITE_REG(PM3StencilData, 0x0); - PM3_SLOW_WRITE_REG(PM3ColorDDAMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCoordMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureIndexMode0, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureIndexMode1, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureReadMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LUTMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureFilterMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureApplicationMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeColorMode1, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeAlphaMode1, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeColorMode0, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeAlphaMode0, 0x0); - PM3_SLOW_WRITE_REG(PM3FogMode, 0x0); - PM3_SLOW_WRITE_REG(PM3ChromaTestMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AlphaTestMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AntialiasMode, 0x0); - PM3_SLOW_WRITE_REG(PM3YUVMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AlphaBlendColorMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AlphaBlendAlphaMode, 0x0); - PM3_SLOW_WRITE_REG(PM3DitherMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LogicalOpMode, 0x0); - PM3_SLOW_WRITE_REG(PM3RouterMode, 0x0); - PM3_SLOW_WRITE_REG(PM3Window, 0x0); - - PM3_SLOW_WRITE_REG(PM3Config2D, 0x0); - - PM3_SLOW_WRITE_REG(PM3SpanColorMask, 0xffffffff); - - PM3_SLOW_WRITE_REG(PM3XBias, 0x0); - PM3_SLOW_WRITE_REG(PM3YBias, 0x0); - PM3_SLOW_WRITE_REG(PM3DeltaControl, 0x0); - - PM3_SLOW_WRITE_REG(PM3BitMaskPattern, 0xffffffff); - - PM3_SLOW_WRITE_REG(PM3FBDestReadEnables, - PM3FBDestReadEnables_E(0xff) | - PM3FBDestReadEnables_R(0xff) | - PM3FBDestReadEnables_ReferenceAlpha(0xff)); - PM3_SLOW_WRITE_REG(PM3FBDestReadBufferAddr0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBDestReadBufferOffset0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBDestReadBufferWidth0, - PM3FBDestReadBufferWidth_Width(l_fb_info-> - current_par-> - width)); - - PM3_SLOW_WRITE_REG(PM3FBDestReadMode, - PM3FBDestReadMode_ReadEnable | - PM3FBDestReadMode_Enable0); - PM3_SLOW_WRITE_REG(PM3FBSourceReadBufferAddr, 0x0); - PM3_SLOW_WRITE_REG(PM3FBSourceReadBufferOffset, 0x0); - PM3_SLOW_WRITE_REG(PM3FBSourceReadBufferWidth, - PM3FBSourceReadBufferWidth_Width(l_fb_info-> - current_par-> - width)); - PM3_SLOW_WRITE_REG(PM3FBSourceReadMode, - PM3FBSourceReadMode_Blocking | - PM3FBSourceReadMode_ReadEnable); - - { - unsigned long rm = 1; - switch (l_fb_info->current_par->depth) { - case 8: - PM3_SLOW_WRITE_REG(PM3PixelSize, - PM3PixelSize_GLOBAL_8BIT); - break; - case 12: - case 15: - case 16: - PM3_SLOW_WRITE_REG(PM3PixelSize, - PM3PixelSize_GLOBAL_16BIT); - break; - case 32: - PM3_SLOW_WRITE_REG(PM3PixelSize, - PM3PixelSize_GLOBAL_32BIT); - break; - default: - DPRINTK(1, "Unsupported depth %d\n", - l_fb_info->current_par->depth); - break; - } - PM3_SLOW_WRITE_REG(PM3RasterizerMode, rm); + if (var->xoffset) { + DPRINTK("xoffset not supported\n"); + return -EINVAL; } - PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0xffffffff); - PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xffffffff); - PM3_SLOW_WRITE_REG(PM3FBWriteMode, - PM3FBWriteMode_WriteEnable | - PM3FBWriteMode_OpaqueSpan | - PM3FBWriteMode_Enable0); - PM3_SLOW_WRITE_REG(PM3FBWriteBufferAddr0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBWriteBufferOffset0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBWriteBufferWidth0, - PM3FBWriteBufferWidth_Width(l_fb_info-> - current_par-> - width)); - - PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, 0x0); - { - unsigned long sofb = (8UL * l_fb_info->fb_size) / - ((depth2bpp(l_fb_info->current_par->depth)) - * l_fb_info->current_par->width); /* size in lines of FB */ - if (sofb > 4095) - PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, 4095); - else - PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, sofb); - - switch (l_fb_info->current_par->depth) { - case 8: - PM3_SLOW_WRITE_REG(PM3DitherMode, - (1 << 10) | (2 << 3)); - break; - case 12: - case 15: - case 16: - PM3_SLOW_WRITE_REG(PM3DitherMode, - (1 << 10) | (1 << 3)); - break; - case 32: - PM3_SLOW_WRITE_REG(PM3DitherMode, - (1 << 10) | (0 << 3)); - break; - default: - DPRINTK(1, "Unsupported depth %d\n", - l_fb_info->current_par->depth); - break; - } + if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { + DPRINTK("interlace not supported\n"); + return -EINVAL; } - PM3_SLOW_WRITE_REG(PM3dXDom, 0x0); - PM3_SLOW_WRITE_REG(PM3dXSub, 0x0); - PM3_SLOW_WRITE_REG(PM3dY, (1 << 16)); - PM3_SLOW_WRITE_REG(PM3StartXDom, 0x0); - PM3_SLOW_WRITE_REG(PM3StartXSub, 0x0); - PM3_SLOW_WRITE_REG(PM3StartY, 0x0); - PM3_SLOW_WRITE_REG(PM3Count, 0x0); - -/* Disable LocalBuffer. better safe than sorry */ - PM3_SLOW_WRITE_REG(PM3LBDestReadMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LBDestReadEnables, 0x0); - PM3_SLOW_WRITE_REG(PM3LBSourceReadMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LBWriteMode, 0x0); - - pm3fb_wait_pm3(l_fb_info); -} + var->xres = (var->xres + 31) & ~31; /* could sometimes be 8 */ + lpitch = var->xres * ((var->bits_per_pixel + 7)>>3); -#ifdef FBCON_HAS_CFB32 -static void pm3fb_cfb32_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u32 c; - - DTRACE; - - sx = sx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - height = height * fontheight(p); - c = ((u32 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - - /* block fills in 32bpp are hard, but in low res (width <= 1600 :-) - we can use 16bpp operations, but not if NoWriteMask is on (SDRAM) */ - if ((l_fb_info->current_par->width > 1600) || - (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)) { - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - } else { - PM3_WAIT(8); - - PM3_WRITE_REG(PM3FBBlockColor, c); - - PM3_WRITE_REG(PM3PixelSize, PM3PixelSize_GLOBAL_16BIT); - - PM3_WRITE_REG(PM3FBWriteBufferWidth0, - PM3FBWriteBufferWidth_Width(l_fb_info-> - current_par-> - width << 1)); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx << 1)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(width << 1)) | - (PM3Render2D_Height(height))); - - PM3_WRITE_REG(PM3FBWriteBufferWidth0, - PM3FBWriteBufferWidth_Width(l_fb_info-> - current_par-> - width)); - - PM3_WRITE_REG(PM3PixelSize, PM3PixelSize_GLOBAL_32BIT); + if (var->xres < 200 || var->xres > 2048) { + DPRINTK("width not supported: %u\n", var->xres); + return -EINVAL; } - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfb32_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int sx, sy; - u32 c; - - DTRACE; - - sx = conp->vc_cols * fontwidth(p); /* right margin */ - sy = conp->vc_rows * fontheight(p); /* bottom margin */ - c = ((u32 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - - if (!bottom_only) { /* right margin top->bottom */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset - (p->var.xoffset + - sx)) | (PM3RectanglePosition_YOffset(p-> - var. - yoffset))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); + if (var->yres < 200 || var->yres > 4095) { + DPRINTK("height not supported: %u\n", var->yres); + return -EINVAL; } - /* bottom margin left -> right */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(p->var.xoffset)) | - (PM3RectanglePosition_YOffset(p->var.yoffset + sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); - - pm3fb_wait_pm3(l_fb_info); -} -#endif /* FBCON_HAS_CFB32 */ -#ifdef FBCON_HAS_CFB16 -static void pm3fb_cfb16_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u32 c; - - DTRACE; - - sx = sx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - height = height * fontheight(p); - c = ((u16 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - c = c | (c << 16); - - PM3_WAIT(4); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3ForegroundColor, c); - else - PM3_WRITE_REG(PM3FBBlockColor, c); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - else - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfb16_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int sx, sy; - u32 c; - - DTRACE; - - sx = conp->vc_cols * fontwidth(p); /* right margin */ - sy = conp->vc_rows * fontheight(p); /* bottom margin */ - c = ((u16 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - c = c | (c << 16); - - if (!bottom_only) { /* right margin top->bottom */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3ForegroundColor, c); - else - PM3_WRITE_REG(PM3FBBlockColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset - (p->var.xoffset + - sx)) | (PM3RectanglePosition_YOffset(p-> - var. - yoffset))); - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); - else - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); + if (lpitch * var->yres_virtual > info->fix.smem_len) { + DPRINTK("no memory for screen (%ux%ux%u)\n", + var->xres, var->yres_virtual, var->bits_per_pixel); + return -EINVAL; } - - /* bottom margin left -> right */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3ForegroundColor, c); - else - PM3_WRITE_REG(PM3FBBlockColor, c); - - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(p->var.xoffset)) | - (PM3RectanglePosition_YOffset(p->var.yoffset + sy))); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); - else - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); - - pm3fb_wait_pm3(l_fb_info); -} -#endif /* FBCON_HAS_CFB16 */ -#ifdef FBCON_HAS_CFB8 -static void pm3fb_cfb8_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u32 c; - - DTRACE; - - sx = sx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - height = height * fontheight(p); - - c = attr_bgcol_ec(p, conp); - c |= c << 8; - c |= c << 16; - - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfb8_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int sx, sy; - u32 c; - - DTRACE; - - sx = conp->vc_cols * fontwidth(p); /* right margin */ - sy = conp->vc_rows * fontheight(p); /* bottom margin */ - c = attr_bgcol_ec(p, conp); - c |= c << 8; - c |= c << 16; - - if (!bottom_only) { /* right margin top->bottom */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset - (p->var.xoffset + - sx)) | (PM3RectanglePosition_YOffset(p-> - var. - yoffset))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); + if (PICOS2KHZ(var->pixclock) > PM3_MAX_PIXCLOCK) { + DPRINTK("pixclock too high (%ldKHz)\n", PICOS2KHZ(var->pixclock)); + return -EINVAL; } - /* bottom margin left -> right */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(p->var.xoffset)) | - (PM3RectanglePosition_YOffset(p->var.yoffset + sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); + var->accel_flags = 0; /* Can't mmap if this is on */ - pm3fb_wait_pm3(l_fb_info); -} -#endif /* FBCON_HAS_CFB8 */ -#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB32) -static void pm3fb_cfbX_bmove(struct display *p, - int sy, int sx, - int dy, int dx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int x_align, o_x, o_y; - - DTRACE; - - sx = sx * fontwidth(p); - dx = dx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - dy = dy * fontheight(p); - height = height * fontheight(p); - - o_x = sx - dx; /*(sx > dx ) ? (sx - dx) : (dx - sx); */ - o_y = sy - dy; /*(sy > dy ) ? (sy - dy) : (dy - sy); */ - - x_align = (sx & 0x1f); - - PM3_WAIT(6); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UserScissorEnable | - PM3Config2D_ForegroundROPEnable | - PM3Config2D_Blocking | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ScissorMinXY, - ((dy & 0x0fff) << 16) | (dx & 0x0fff)); - PM3_WRITE_REG(PM3ScissorMaxXY, - (((dy + height) & 0x0fff) << 16) | - ((dx + width) & 0x0fff)); - - PM3_WRITE_REG(PM3FBSourceReadBufferOffset, - PM3FBSourceReadBufferOffset_XOffset(o_x) | - PM3FBSourceReadBufferOffset_YOffset(o_y)); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(dx - x_align)) | - (PM3RectanglePosition_YOffset(dy))); - - PM3_WRITE_REG(PM3Render2D, - ((sx > dx) ? PM3Render2D_XPositive : 0) | - ((sy > dy) ? PM3Render2D_YPositive : 0) | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - PM3Render2D_FBSourceReadEnable | - (PM3Render2D_Width(width + x_align)) | - (PM3Render2D_Height(height))); - - pm3fb_wait_pm3(l_fb_info); + DPRINTK("Checking graphics mode at %dx%d depth %d\n", + var->xres, var->yres, var->bits_per_pixel); + return 0; } -static void pm3fb_cfbX_putc(struct vc_data *conp, struct display *p, - int c, int yy, int xx) +static int pm3fb_set_par(struct fb_info *info) { - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u8 *cdat, asx = 0, asy = 0, o_x = 0, o_y = 0; - u32 fgx, bgx, ldat; - int sx, sy, i; + struct pm3_par *par = info->par; + const u32 xres = (info->var.xres + 31) & ~31; + const int depth = (info->var.bits_per_pixel + 7) & ~7; - DTRACE; + par->base = pm3fb_shift_bpp(info->var.bits_per_pixel, + (info->var.yoffset * xres) + + info->var.xoffset); + par->video = 0; - if (l_fb_info->current_par->depth == 8) - fgx = attr_fgcol(p, c); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - fgx = ((u16 *) p->dispsw_data)[attr_fgcol(p, c)]; + if (info->var.sync & FB_SYNC_HOR_HIGH_ACT) + par->video |= PM3VideoControl_HSYNC_ACTIVE_HIGH; else - fgx = ((u32 *) p->dispsw_data)[attr_fgcol(p, c)]; + par->video |= PM3VideoControl_HSYNC_ACTIVE_LOW; - PM3_COLOR(fgx); - - if (l_fb_info->current_par->depth == 8) - bgx = attr_bgcol(p, c); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - bgx = ((u16 *) p->dispsw_data)[attr_bgcol(p, c)]; + if (info->var.sync & FB_SYNC_VERT_HIGH_ACT) + par->video |= PM3VideoControl_VSYNC_ACTIVE_HIGH; else - bgx = ((u32 *) p->dispsw_data)[attr_bgcol(p, c)]; - - PM3_COLOR(bgx); - - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable | PM3Config2D_OpaqueSpan); - - PM3_WRITE_REG(PM3ForegroundColor, fgx); - PM3_WRITE_REG(PM3FillBackgroundColor, bgx); - - /* WARNING : address select X need to specify 8 bits for fontwidth <= 8 */ - /* and 16 bits for fontwidth <= 16 */ - /* same in _putcs, same for Y and fontheight */ - if (fontwidth(p) <= 8) - asx = 2; - else if (fontwidth(p) <= 16) - asx = 3; /* look OK */ - if (fontheight(p) <= 8) - asy = 2; - else if (fontheight(p) <= 16) - asy = 3; /* look OK */ - else if (fontheight(p) <= 32) - asy = 4; /* look OK */ - - sx = xx * fontwidth(p); - sy = yy * fontheight(p); - - if (fontwidth(p) <= 8) - o_x = (8 - (sx & 0x7)) & 0x7; - else if (fontwidth(p) <= 16) - o_x = (16 - (sx & 0xF)) & 0xF; - if (fontheight(p) <= 8) - o_y = (8 - (sy & 0x7)) & 0x7; - else if (fontheight(p) <= 16) - o_y = (16 - (sy & 0xF)) & 0xF; - else if (fontheight(p) <= 32) - o_y = (32 - (sy & 0x1F)) & 0x1F; - - PM3_WRITE_REG(PM3AreaStippleMode, (o_x << 7) | (o_y << 12) | /* x_offset, y_offset in pattern */ - (1 << 18) | /* BE */ - 1 | (asx << 1) | (asy << 4) | /* address select x/y */ - (1 << 20)); /* OpaqueSpan */ - - if (fontwidth(p) <= 8) { - cdat = p->fontdata + (c & p->charmask) * fontheight(p); - } else { - cdat = - p->fontdata + - ((c & p->charmask) * (fontheight(p) << 1)); - } - - PM3_WAIT(2 + fontheight(p)); - - for (i = 0; i < fontheight(p); i++) { /* assume fontheight <= 32 */ - if (fontwidth(p) <= 8) { - ldat = *cdat++; - } else { /* assume fontwidth <= 16 ATM */ - - ldat = ((*cdat++) << 8); - ldat |= *cdat++; - } - PM3_WRITE_REG(AreaStipplePattern_indexed(i), ldat); - } - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); + par->video |= PM3VideoControl_VSYNC_ACTIVE_LOW; - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_AreaStippleEnable | - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(fontwidth(p))) | - (PM3Render2D_Height(fontheight(p)))); - - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfbX_putcs(struct vc_data *conp, struct display *p, - const unsigned short *s, int count, int yy, - int xx) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u8 *cdat, asx = 0, asy = 0, o_x = 0, o_y = 0; - u32 fgx, bgx, ldat; - int sx, sy, i, j; - u16 sc; - - DTRACE; - - sc = scr_readw(s); - if (l_fb_info->current_par->depth == 8) - fgx = attr_fgcol(p, sc); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - fgx = ((u16 *) p->dispsw_data)[attr_fgcol(p, sc)]; - else - fgx = ((u32 *) p->dispsw_data)[attr_fgcol(p, sc)]; - - PM3_COLOR(fgx); - - if (l_fb_info->current_par->depth == 8) - bgx = attr_bgcol(p, sc); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - bgx = ((u16 *) p->dispsw_data)[attr_bgcol(p, sc)]; + if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) + par->video |= PM3VideoControl_LINE_DOUBLE_ON; else - bgx = ((u32 *) p->dispsw_data)[attr_bgcol(p, sc)]; - - PM3_COLOR(bgx); - - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable | - PM3Config2D_OpaqueSpan); - - PM3_WRITE_REG(PM3ForegroundColor, fgx); - PM3_WRITE_REG(PM3FillBackgroundColor, bgx); - - /* WARNING : address select X need to specify 8 bits for fontwidth <= 8 */ - /* and 16 bits for fontwidth <= 16 */ - /* same in _putc, same for Y and fontheight */ - if (fontwidth(p) <= 8) - asx = 2; - else if (fontwidth(p) <= 16) - asx = 3; /* look OK */ - if (fontheight(p) <= 8) - asy = 2; - else if (fontheight(p) <= 16) - asy = 3; /* look OK */ - else if (fontheight(p) <= 32) - asy = 4; /* look OK */ - - sy = yy * fontheight(p); - - if (fontheight(p) <= 8) - o_y = (8 - (sy & 0x7)) & 0x7; - else if (fontheight(p) <= 16) - o_y = (16 - (sy & 0xF)) & 0xF; - else if (fontheight(p) <= 32) - o_y = (32 - (sy & 0x1F)) & 0x1F; - - for (j = 0; j < count; j++) { - sc = scr_readw(s + j); - if (fontwidth(p) <= 8) - cdat = p->fontdata + - (sc & p->charmask) * fontheight(p); - else - cdat = p->fontdata + - ((sc & p->charmask) * fontheight(p) << 1); - - sx = (xx + j) * fontwidth(p); - - if (fontwidth(p) <= 8) - o_x = (8 - (sx & 0x7)) & 0x7; - else if (fontwidth(p) <= 16) - o_x = (16 - (sx & 0xF)) & 0xF; - - PM3_WAIT(3 + fontheight(p)); - - PM3_WRITE_REG(PM3AreaStippleMode, (o_x << 7) | (o_y << 12) | /* x_offset, y_offset in pattern */ - (1 << 18) | /* BE */ - 1 | (asx << 1) | (asy << 4) | /* address select x/y */ - (1 << 20)); /* OpaqueSpan */ - - for (i = 0; i < fontheight(p); i++) { /* assume fontheight <= 32 */ - if (fontwidth(p) <= 8) { - ldat = *cdat++; - } else { /* assume fontwidth <= 16 ATM */ - ldat = ((*cdat++) << 8); - ldat |= *cdat++; - } - PM3_WRITE_REG(AreaStipplePattern_indexed(i), ldat); - } - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_AreaStippleEnable | - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(fontwidth(p))) | - (PM3Render2D_Height(fontheight(p)))); - } - pm3fb_wait_pm3(l_fb_info); -} + par->video |= PM3VideoControl_LINE_DOUBLE_OFF; -static void pm3fb_cfbX_revc(struct display *p, int xx, int yy) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - - xx = xx * fontwidth(p); - yy = yy * fontheight(p); - - if (l_fb_info->current_par->depth == 8) - { - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0x0F0F0F0F); - else - PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0x0F0F0F0F); + if (info->var.activate == FB_ACTIVATE_NOW) + par->video |= PM3VideoControl_ENABLE; + else { + par->video |= PM3VideoControl_DISABLE; + DPRINTK("PM3Video disabled\n"); } - - PM3_WAIT(3); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0xa)) | /* Oxa is GXinvert */ - PM3Config2D_FBDestReadEnable | - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(xx)) | - (PM3RectanglePosition_YOffset(yy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(fontwidth(p))) | - (PM3Render2D_Height(fontheight(p)))); - - pm3fb_wait_pm3(l_fb_info); - - if (l_fb_info->current_par->depth == 8) - { - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0xFFFFFFFF); - else - PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xFFFFFFFF); - } -} - -#endif /* FBCON_HAS_CFB8 || FBCON_HAS_CFB16 || FBCON_HAS_CFB32 */ -#endif /* PM3FB_USE_ACCEL */ -/* *********************************** */ -/* ***** pre-init board(s) setup ***** */ -/* *********************************** */ - -static void pm3fb_mode_setup(char *mode, unsigned long board_num) -{ - struct pm3fb_info *l_fb_info = &(fb_info[board_num]); - struct pm3fb_par *l_fb_par = &(current_par[board_num]); - unsigned long i = 0; - - current_par_valid[board_num] = 0; - - if (!strncmp(mode, "current", 7)) { - l_fb_info->use_current = 1; /* default w/ OpenFirmware */ - } else { - while ((mode_base[i].name[0]) - && (!current_par_valid[board_num])) { - if (! - (strncmp - (mode, mode_base[i].name, - strlen(mode_base[i].name)))) { - memcpy(l_fb_par, &(mode_base[i].user_mode), - sizeof(struct pm3fb_par)); - current_par_valid[board_num] = 1; - DPRINTK(2, "Mode set to %s\n", - mode_base[i].name); - } - i++; - } - DASSERT(current_par_valid[board_num], - "Valid mode on command line\n"); - } -} - -static void pm3fb_pciid_setup(char *pciid, unsigned long board_num) -{ - short l_bus = -1, l_slot = -1, l_func = -1; - char *next; - - if (pciid) { - l_bus = simple_strtoul(pciid, &next, 10); - if (next && (next[0] == ':')) { - pciid = next + 1; - l_slot = simple_strtoul(pciid, &next, 10); - if (next && (next[0] == ':')) { - pciid = next + 1; - l_func = - simple_strtoul(pciid, (char **) NULL, - 10); - } - } - } else - return; - - if ((l_bus >= 0) && (l_slot >= 0) && (l_func >= 0)) { - bus[board_num] = l_bus; - slot[board_num] = l_slot; - func[board_num] = l_func; - DPRINTK(2, "Board #%ld will be PciId: %hd:%hd:%hd\n", - board_num, l_bus, l_slot, l_func); - } else { - DPRINTK(1, "Invalid PciId: %hd:%hd:%hd for board #%ld\n", - l_bus, l_slot, l_func, board_num); - } -} - -static void pm3fb_font_setup(char *lf, unsigned long board_num) -{ - unsigned long lfs = strlen(lf); - - if (lfs > (PM3_FONTNAME_SIZE - 1)) { - DPRINTK(1, "Fontname %s too long\n", lf); - return; - } - strlcpy(fontn[board_num], lf, lfs + 1); -} - -static void pm3fb_bootdepth_setup(char *bds, unsigned long board_num) -{ - unsigned long bd = simple_strtoul(bds, (char **) NULL, 10); - - if (!(depth_supported(bd))) { - printk(KERN_WARNING "pm3fb: ignoring invalid depth %s for board #%ld\n", - bds, board_num); - return; - } - depth[board_num] = bd; -} - -static void pm3fb_forcesize_setup(char *bds, unsigned long board_num) -{ - unsigned long bd = simple_strtoul(bds, (char **) NULL, 10); - - if (bd > 64) { - printk(KERN_WARNING "pm3fb: ignoring invalid memory size %s for board #%ld\n", - bds, board_num); - return; - } - forcesize[board_num] = bd; -} - -static char *pm3fb_boardnum_setup(char *options, unsigned long *bn) -{ - char *next; - - if (!(isdigit(options[0]))) { - (*bn) = 0; - return (options); - } - - (*bn) = simple_strtoul(options, &next, 10); - - if (next && (next[0] == ':') && ((*bn) >= 0) - && ((*bn) <= PM3_MAX_BOARD)) { - DPRINTK(2, "Board_num seen as %ld\n", (*bn)); - return (next + 1); - } else { - (*bn) = 0; - DPRINTK(2, "Board_num default to %ld\n", (*bn)); - return (options); - } -} - -static void pm3fb_real_setup(char *options) -{ - char *next; - unsigned long i, bn; - struct pm3fb_info *l_fb_info; - - DTRACE; - - DPRINTK(2, "Options : %s\n", options); - - for (i = 0; i < PM3_MAX_BOARD; i++) { - l_fb_info = &(fb_info[i]); - memset(l_fb_info, 0, sizeof(struct pm3fb_info)); - l_fb_info->gen.fbhw = &pm3fb_switch; - l_fb_info->board_num = i; - current_par_valid[i] = 0; - slot[i] = -1; - func[i] = -1; - bus[i] = -1; - disable[i] = 0; - noaccel[i] = 0; - fontn[i][0] = '\0'; - depth[i] = 0; - l_fb_info->current_par = &(current_par[i]); - } - - /* eat up prefix pm3fb and whatever is used as separator i.e. :,= */ - if (!strncmp(options, "pm3fb", 5)) { - options += 5; - while (((*options) == ',') || ((*options) == ':') - || ((*options) == '=')) - options++; - } - - while (options) { - bn = 0; - if ((next = strchr(options, ','))) { - (*next) = '\0'; - next++; - } - - if (!strncmp(options, "mode:", 5)) { - options = pm3fb_boardnum_setup(options + 5, &bn); - DPRINTK(2, "Setting mode for board #%ld\n", bn); - pm3fb_mode_setup(options, bn); - } else if (!strncmp(options, "off:", 4)) { - options = pm3fb_boardnum_setup(options + 4, &bn); - DPRINTK(2, "Disabling board #%ld\n", bn); - disable[bn] = 1; - } else if (!strncmp(options, "off", 3)) { /* disable everything */ - for (i = 0; i < PM3_MAX_BOARD; i++) - disable[i] = 1; - } else if (!strncmp(options, "disable:", 8)) { - options = pm3fb_boardnum_setup(options + 8, &bn); - DPRINTK(2, "Disabling board #%ld\n", bn); - disable[bn] = 1; - } else if (!strncmp(options, "pciid:", 6)) { - options = pm3fb_boardnum_setup(options + 6, &bn); - DPRINTK(2, "Setting PciID for board #%ld\n", bn); - pm3fb_pciid_setup(options, bn); - } else if (!strncmp(options, "noaccel:", 8)) { - options = pm3fb_boardnum_setup(options + 8, &bn); - noaccel[bn] = 1; - } else if (!strncmp(options, "font:", 5)) { - options = pm3fb_boardnum_setup(options + 5, &bn); - pm3fb_font_setup(options, bn); - } else if (!strncmp(options, "depth:", 6)) { - options = pm3fb_boardnum_setup(options + 6, &bn); - pm3fb_bootdepth_setup(options, bn); - } else if (!strncmp(options, "printtimings", 12)) { - printtimings = 1; - } else if (!strncmp(options, "flatpanel:", 10)) { - options = pm3fb_boardnum_setup(options + 10, &bn); - flatpanel[bn] = 1; - } else if (!strncmp(options, "forcesize:", 10)) { - options = pm3fb_boardnum_setup(options + 10, &bn); - pm3fb_forcesize_setup(options, bn); - } - options = next; - } -} - -/* ********************************************** */ -/* ***** framebuffer API standard functions ***** */ -/* ********************************************** */ - -static int pm3fb_encode_fix(struct fb_fix_screeninfo *fix, - const void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - struct pm3fb_par *p = (struct pm3fb_par *) par; - - DTRACE; - - strcpy(fix->id, permedia3_name); - fix->smem_start = (unsigned long)l_fb_info->p_fb; - fix->smem_len = l_fb_info->fb_size; - fix->mmio_start = (unsigned long)l_fb_info->pIOBase; - fix->mmio_len = PM3_REGS_SIZE; -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - fix->accel = FB_ACCEL_3DLABS_PERMEDIA3; - else -#endif /* PM3FB_USE_ACCEL */ - fix->accel = FB_ACCEL_NONE; - fix->type = FB_TYPE_PACKED_PIXELS; - fix->visual = - (p->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; - if (current_par_valid[l_fb_info->board_num]) - fix->line_length = - l_fb_info->current_par->width * - depth2ByPP(l_fb_info->current_par->depth); - else - fix->line_length = 0; - fix->xpanstep = 64 / depth2bpp(p->depth); - fix->ypanstep = 1; - fix->ywrapstep = 0; - return (0); -} - -static int pm3fb_decode_var(const struct fb_var_screeninfo *var, - void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - struct pm3fb_par *p = (struct pm3fb_par *) par; - struct pm3fb_par temp_p; - u32 xres; - - DTRACE; - - DASSERT((var != NULL), "fb_var_screeninfo* not NULL"); - DASSERT((p != NULL), "pm3fb_par* not NULL"); - DASSERT((l_fb_info != NULL), "pm3fb_info* not NULL"); - - memset(&temp_p, 0, sizeof(struct pm3fb_par)); - temp_p.width = (var->xres_virtual + 7) & ~7; - temp_p.height = var->yres_virtual; - - if (!(depth_supported(var->bits_per_pixel))) /* round unsupported up to a multiple of 8 */ - temp_p.depth = depth2bpp(var->bits_per_pixel); - else - temp_p.depth = var->bits_per_pixel; - - temp_p.depth = (temp_p.depth > 32) ? 32 : temp_p.depth; /* max 32 */ - temp_p.depth = (temp_p.depth == 24) ? 32 : temp_p.depth; /* 24 unsupported, round-up to 32 */ - - if ((temp_p.depth == 16) && (var->red.length == 5) && (var->green.length == 5) && (var->blue.length == 5)) - temp_p.depth = 15; /* RGBA 5551 is stored as depth 15 */ - - if ((temp_p.depth == 16) && (var->red.length == 4) && (var->green.length == 4) && (var->blue.length == 4)) - temp_p.depth = 12; /* RGBA 4444 is stored as depth 12 */ - - - DPRINTK(2, - "xres: %d, yres: %d, vxres: %d, vyres: %d ; xoffset:%d, yoffset: %d\n", - var->xres, var->yres, var->xres_virtual, var->yres_virtual, - var->xoffset, var->yoffset); - - xres = (var->xres + 31) & ~31; - if (temp_p.width < xres + var->xoffset) - temp_p.width = xres + var->xoffset; - if (temp_p.height < var->yres + var->yoffset) - temp_p.height = var->yres + var->yoffset; - - if (temp_p.width > 2048) { - DPRINTK(1, "virtual width not supported: %u\n", - temp_p.width); - return (-EINVAL); - } - if (var->yres < 200) { - DPRINTK(1, "height not supported: %u\n", (u32) var->yres); - return (-EINVAL); - } - if (temp_p.height < 200 || temp_p.height > 4095) { - DPRINTK(1, "virtual height not supported: %u\n", - temp_p.height); - return (-EINVAL); - } - if (!(depth_supported(temp_p.depth))) { - DPRINTK(1, "depth not supported: %u\n", temp_p.depth); - return (-EINVAL); - } - if ((temp_p.width * temp_p.height * depth2ByPP(temp_p.depth)) > - l_fb_info->fb_size) { - DPRINTK(1, "no memory for screen (%ux%ux%u)\n", - temp_p.width, temp_p.height, temp_p.depth); - return (-EINVAL); - } - - if ((!var->pixclock) || - (!var->right_margin) || - (!var->hsync_len) || - (!var->left_margin) || - (!var->lower_margin) || - (!var->vsync_len) || (!var->upper_margin) - ) { - unsigned long i = 0, done = 0; - printk(KERN_WARNING "pm3fb: refusing to use a likely wrong timing\n"); - - while ((mode_base[i].user_mode.width) && !done) { - if ((mode_base[i].user_mode.width == temp_p.width) - && (mode_base[i].user_mode.height == - temp_p.height)) { - printk(KERN_NOTICE "pm3fb: using close match %s\n", - mode_base[i].name); - temp_p = mode_base[i].user_mode; - done = 1; - } - i++; - } - if (!done) - return (-EINVAL); - } else { - temp_p.pixclock = PICOS2KHZ(var->pixclock); - if (temp_p.pixclock > PM3_MAX_PIXCLOCK) { - DPRINTK(1, "pixclock too high (%uKHz)\n", - temp_p.pixclock); - return (-EINVAL); - } - - temp_p.hsstart = var->right_margin; - temp_p.hsend = var->right_margin + var->hsync_len; - temp_p.hbend = - var->right_margin + var->hsync_len + var->left_margin; - temp_p.htotal = xres + temp_p.hbend; - - temp_p.vsstart = var->lower_margin; - temp_p.vsend = var->lower_margin + var->vsync_len; - temp_p.vbend = - var->lower_margin + var->vsync_len + var->upper_margin; - temp_p.vtotal = var->yres + temp_p.vbend; - - temp_p.stride = temp_p.width; - - DPRINTK(2, "Using %d * %d, %d Khz, stride is %08x\n", - temp_p.width, temp_p.height, temp_p.pixclock, - temp_p.stride); - - temp_p.base = - pm3fb_Shiftbpp(l_fb_info, temp_p.depth, - (var->yoffset * xres) + var->xoffset); - - temp_p.video = 0; - - if (var->sync & FB_SYNC_HOR_HIGH_ACT) - temp_p.video |= PM3VideoControl_HSYNC_ACTIVE_HIGH; - else - temp_p.video |= PM3VideoControl_HSYNC_ACTIVE_LOW; - - if (var->sync & FB_SYNC_VERT_HIGH_ACT) - temp_p.video |= PM3VideoControl_VSYNC_ACTIVE_HIGH; - else - temp_p.video |= PM3VideoControl_VSYNC_ACTIVE_LOW; - - if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { - DPRINTK(1, "Interlaced mode not supported\n\n"); - return (-EINVAL); - } - - if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) - temp_p.video |= PM3VideoControl_LINE_DOUBLE_ON; - else - temp_p.video |= PM3VideoControl_LINE_DOUBLE_OFF; - - if (var->activate == FB_ACTIVATE_NOW) - temp_p.video |= PM3VideoControl_ENABLE; - else { - temp_p.video |= PM3VideoControl_DISABLE; - DPRINTK(2, "PM3Video disabled\n"); - } - - switch (temp_p.depth) { - case 8: - temp_p.video |= PM3VideoControl_PIXELSIZE_8BIT; - break; - case 12: - case 15: - case 16: - temp_p.video |= PM3VideoControl_PIXELSIZE_16BIT; - break; - case 32: - temp_p.video |= PM3VideoControl_PIXELSIZE_32BIT; - break; - default: - DPRINTK(1, "Unsupported depth\n"); - break; - } - } - (*p) = temp_p; - -#ifdef PM3FB_USE_ACCEL - if (var->accel_flags & FB_ACCELF_TEXT) - noaccel[l_fb_info->board_num] = 0; - else - noaccel[l_fb_info->board_num] = 1; -#endif /* PM3FB_USE_ACCEL */ - - return (0); -} - -static void pm3fb_encode_depth(struct fb_var_screeninfo *var, long d) -{ - switch (d) { + switch (depth) { case 8: - var->red.length = var->green.length = var->blue.length = 8; - var->red.offset = var->green.offset = var->blue.offset = 0; - var->transp.offset = var->transp.length = 0; + par->video |= PM3VideoControl_PIXELSIZE_8BIT; break; - case 12: - var->red.offset = 8; - var->red.length = 4; - var->green.offset = 4; - var->green.length = 4; - var->blue.offset = 0; - var->blue.length = 4; - var->transp.offset = 12; - var->transp.length = 4; - break; - case 15: - var->red.offset = 10; - var->red.length = 5; - var->green.offset = 5; - var->green.length = 5; - var->blue.offset = 0; - var->blue.length = 5; - var->transp.offset = 15; - var->transp.length = 1; - break; - case 16: - var->red.offset = 11; - var->red.length = 5; - var->green.offset = 5; - var->green.length = 6; - var->blue.offset = 0; - var->blue.length = 5; - var->transp.offset = var->transp.length = 0; + par->video |= PM3VideoControl_PIXELSIZE_16BIT; break; - case 32: - var->transp.offset = 24; - var->red.offset = 16; - var->green.offset = 8; - var->blue.offset = 0; - var->red.length = var->green.length = - var->blue.length = var->transp.length = 8; + par->video |= PM3VideoControl_PIXELSIZE_32BIT; break; - default: - DPRINTK(1, "Unsupported depth %ld\n", d); + DPRINTK("Unsupported depth\n"); break; } -} -static int pm3fb_encode_var(struct fb_var_screeninfo *var, - const void *par, struct fb_info_gen *info) -{ - struct pm3fb_par *p = (struct pm3fb_par *) par; - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - u32 base; - - DTRACE; - - DASSERT((var != NULL), "fb_var_screeninfo* not NULL"); - DASSERT((p != NULL), "pm3fb_par* not NULL"); - DASSERT((info != NULL), "fb_info_gen* not NULL"); - - memset(var, 0, sizeof(struct fb_var_screeninfo)); - -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - var->accel_flags |= FB_ACCELF_TEXT; -#endif /* PM3FB_USE_ACCEL */ - - var->xres_virtual = p->width; - var->yres_virtual = p->height; - var->xres = p->htotal - p->hbend; - var->yres = p->vtotal - p->vbend; - - DPRINTK(2, "xres = %d, yres : %d\n", var->xres, var->yres); - - var->right_margin = p->hsstart; - var->hsync_len = p->hsend - p->hsstart; - var->left_margin = p->hbend - p->hsend; - var->lower_margin = p->vsstart; - var->vsync_len = p->vsend - p->vsstart; - var->upper_margin = p->vbend - p->vsend; - var->bits_per_pixel = depth2bpp(p->depth); - - pm3fb_encode_depth(var, p->depth); - - base = pm3fb_Unshiftbpp(l_fb_info, p->depth, p->base); - - var->xoffset = base % var->xres; - var->yoffset = base / var->xres; - - var->height = var->width = -1; - - var->pixclock = KHZ2PICOS(p->pixclock); - - if ((p->video & PM3VideoControl_HSYNC_MASK) == - PM3VideoControl_HSYNC_ACTIVE_HIGH) - var->sync |= FB_SYNC_HOR_HIGH_ACT; - if ((p->video & PM3VideoControl_VSYNC_MASK) == - PM3VideoControl_VSYNC_ACTIVE_HIGH) - var->sync |= FB_SYNC_VERT_HIGH_ACT; - if (p->video & PM3VideoControl_LINE_DOUBLE_ON) - var->vmode = FB_VMODE_DOUBLE; - - return (0); -} - -static void pm3fb_get_par(void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; + info->fix.visual = + (depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + info->fix.line_length = ((info->var.xres_virtual + 7) & ~7) + * depth / 8; - if (!current_par_valid[l_fb_info->board_num]) { - if (l_fb_info->use_current) - pm3fb_read_mode(l_fb_info, l_fb_info->current_par); - else - memcpy(l_fb_info->current_par, - &(mode_base[0].user_mode), - sizeof(struct pm3fb_par)); - current_par_valid[l_fb_info->board_num] = 1; - } - *((struct pm3fb_par *) par) = *(l_fb_info->current_par); -} - -static void pm3fb_set_par(const void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; - - *(l_fb_info->current_par) = *((struct pm3fb_par *) par); - current_par_valid[l_fb_info->board_num] = 1; - - pm3fb_write_mode(l_fb_info); - -#ifdef PM3FB_USE_ACCEL - pm3fb_init_engine(l_fb_info); -#endif /* PM3FB_USE_ACCEL */ -} - -static void pm3fb_set_color(struct pm3fb_info *l_fb_info, - unsigned char regno, unsigned char r, - unsigned char g, unsigned char b) -{ - DTRACE; - - PM3_SLOW_WRITE_REG(PM3RD_PaletteWriteAddress, regno); - PM3_SLOW_WRITE_REG(PM3RD_PaletteData, r); - PM3_SLOW_WRITE_REG(PM3RD_PaletteData, g); - PM3_SLOW_WRITE_REG(PM3RD_PaletteData, b); -} - -static int pm3fb_getcolreg(unsigned regno, unsigned *red, unsigned *green, - unsigned *blue, unsigned *transp, - struct fb_info *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; - - if (regno < 256) { - *red = - l_fb_info->palette[regno].red << 8 | l_fb_info-> - palette[regno].red; - *green = - l_fb_info->palette[regno].green << 8 | l_fb_info-> - palette[regno].green; - *blue = - l_fb_info->palette[regno].blue << 8 | l_fb_info-> - palette[regno].blue; - *transp = - l_fb_info->palette[regno].transp << 8 | l_fb_info-> - palette[regno].transp; - } - return (regno > 255); +/* pm3fb_clear_memory(info, 0);*/ + pm3fb_clear_colormap(par, 0, 0, 0); + PM3_WRITE_DAC_REG(par, PM3RD_CursorMode, + PM3RD_CursorMode_CURSOR_DISABLE); + pm3fb_write_mode(info); + return 0; } static int pm3fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info) { - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; + struct pm3_par *par = info->par; + + if (regno >= 256) /* no. of hw registers */ + return -EINVAL; + + /* grayscale works only partially under directcolor */ + if (info->var.grayscale) { + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + } + + /* Directcolor: + * var->{color}.offset contains start of bitfield + * var->{color}.length contains length of bitfield + * {hardwarespecific} contains width of DAC + * pseudo_palette[X] is programmed to (X << red.offset) | + * (X << green.offset) | + * (X << blue.offset) + * RAMDAC[X] is programmed to (red, green, blue) + * color depth = SUM(var->{color}.length) + * + * Pseudocolor: + * var->{color}.offset is 0 + * var->{color}.length contains width of DAC or the number of unique + * colors available (color depth) + * pseudo_palette is not used + * RAMDAC[X] is programmed to (red, green, blue) + * color depth = var->{color}.length + */ - if (regno < 16) { - switch (l_fb_info->current_par->depth) { -#ifdef FBCON_HAS_CFB8 + /* + * This is the point where the color is converted to something that + * is acceptable by the hardware. + */ +#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) + red = CNVT_TOHW(red, info->var.red.length); + green = CNVT_TOHW(green, info->var.green.length); + blue = CNVT_TOHW(blue, info->var.blue.length); + transp = CNVT_TOHW(transp, info->var.transp.length); +#undef CNVT_TOHW + + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) { + u32 v; + + if (regno >= 16) + return -EINVAL; + + v = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + + switch (info->var.bits_per_pixel) { case 8: break; -#endif -#ifdef FBCON_HAS_CFB16 - case 12: - l_fb_info->cmap.cmap12[regno] = - (((u32) red & 0xf000) >> 4) | - (((u32) green & 0xf000) >> 8) | - (((u32) blue & 0xf000) >> 12); - break; - - case 15: - l_fb_info->cmap.cmap15[regno] = - (((u32) red & 0xf800) >> 1) | - (((u32) green & 0xf800) >> 6) | - (((u32) blue & 0xf800) >> 11); - break; - case 16: - l_fb_info->cmap.cmap16[regno] = - ((u32) red & 0xf800) | - (((u32) green & 0xfc00) >> 5) | - (((u32) blue & 0xf800) >> 11); - break; -#endif -#ifdef FBCON_HAS_CFB32 + case 24: case 32: - l_fb_info->cmap.cmap32[regno] = - (((u32) transp & 0xff00) << 16) | - (((u32) red & 0xff00) << 8) | - (((u32) green & 0xff00)) | - (((u32) blue & 0xff00) >> 8); - break; -#endif - default: - DPRINTK(1, "bad depth %u\n", - l_fb_info->current_par->depth); + ((u32*)(info->pseudo_palette))[regno] = v; break; } + return 0; } - if (regno < 256) { - l_fb_info->palette[regno].red = red >> 8; - l_fb_info->palette[regno].green = green >> 8; - l_fb_info->palette[regno].blue = blue >> 8; - l_fb_info->palette[regno].transp = transp >> 8; - if (l_fb_info->current_par->depth == 8) - pm3fb_set_color(l_fb_info, regno, red >> 8, - green >> 8, blue >> 8); - } - return (regno > 255); + else if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) + pm3fb_set_color(par, regno, red, green, blue); + + return 0; } -static int pm3fb_blank(int blank_mode, struct fb_info_gen *info) +static int pm3fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) { - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - u32 video; - - DTRACE; + struct pm3_par *par = info->par; + const u32 xres = (var->xres + 31) & ~31; - if (!current_par_valid[l_fb_info->board_num]) - return (1); + par->base = pm3fb_shift_bpp(var->bits_per_pixel, + (var->yoffset * xres) + + var->xoffset); + PM3_SLOW_WRITE_REG(par, PM3ScreenBase, par->base); + return 0; +} - video = l_fb_info->current_par->video; +static int pm3fb_blank(int blank_mode, struct fb_info *info) +{ + struct pm3_par *par = info->par; + u32 video = par->video; /* * Oxygen VX1 - it appears that setting PM3VideoControl and @@ -3181,454 +663,345 @@ static int pm3fb_blank(int blank_mode, struct fb_info_gen *info) video |= PM3VideoControl_HSYNC_ACTIVE_HIGH | PM3VideoControl_VSYNC_ACTIVE_HIGH; - if (blank_mode > 0) { - switch (blank_mode - 1) { - - case VESA_NO_BLANKING: /* FIXME */ - video = video & ~(PM3VideoControl_ENABLE); - break; - - case VESA_HSYNC_SUSPEND: - video = video & ~(PM3VideoControl_HSYNC_MASK | - PM3VideoControl_BLANK_ACTIVE_LOW); - break; - case VESA_VSYNC_SUSPEND: - video = video & ~(PM3VideoControl_VSYNC_MASK | - PM3VideoControl_BLANK_ACTIVE_LOW); - break; - case VESA_POWERDOWN: - video = video & ~(PM3VideoControl_HSYNC_MASK | - PM3VideoControl_VSYNC_MASK | - PM3VideoControl_BLANK_ACTIVE_LOW); - break; - default: - DPRINTK(1, "Unsupported blanking %d\n", - blank_mode); - return (1); - break; - } - } - - PM3_SLOW_WRITE_REG(PM3VideoControl, video); - - return (0); -} - -static void pm3fb_set_disp(const void *par, struct display *disp, - struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - struct pm3fb_par *p = (struct pm3fb_par *) par; - u32 flags; - - DTRACE; - - local_irq_save(flags); - info->info.screen_base = l_fb_info->v_fb; - switch (p->depth) { -#ifdef FBCON_HAS_CFB8 - case 8: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb8; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb8; + switch (blank_mode) { + case FB_BLANK_UNBLANK: + video = video | PM3VideoControl_ENABLE; break; -#endif -#ifdef FBCON_HAS_CFB16 - case 12: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb16; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb16; - disp->dispsw_data = l_fb_info->cmap.cmap12; + case FB_BLANK_NORMAL: /* FIXME */ + video = video & ~(PM3VideoControl_ENABLE); break; - case 15: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb16; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb16; - disp->dispsw_data = l_fb_info->cmap.cmap15; + case FB_BLANK_HSYNC_SUSPEND: + video = video & ~(PM3VideoControl_HSYNC_MASK | + PM3VideoControl_BLANK_ACTIVE_LOW); break; - case 16: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb16; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb16; - disp->dispsw_data = l_fb_info->cmap.cmap16; + case FB_BLANK_VSYNC_SUSPEND: + video = video & ~(PM3VideoControl_VSYNC_MASK | + PM3VideoControl_BLANK_ACTIVE_LOW); break; -#endif -#ifdef FBCON_HAS_CFB32 - case 32: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb32; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb32; - disp->dispsw_data = l_fb_info->cmap.cmap32; + case FB_BLANK_POWERDOWN: + video = video & ~(PM3VideoControl_HSYNC_MASK | + PM3VideoControl_VSYNC_MASK | + PM3VideoControl_BLANK_ACTIVE_LOW); break; -#endif /* FBCON_HAS_CFB32 */ default: - disp->dispsw = &fbcon_dummy; - DPRINTK(1, "Invalid depth, using fbcon_dummy\n"); - break; + DPRINTK("Unsupported blanking %d\n", blank_mode); + return 1; } - local_irq_restore(flags); + + PM3_SLOW_WRITE_REG(par,PM3VideoControl, video); + + return 0; } -/* */ -static void pm3fb_detect(void) -{ - struct pci_dev *dev_array[PM3_MAX_BOARD]; - struct pci_dev *dev = NULL; - struct pm3fb_info *l_fb_info = &(fb_info[0]); - unsigned long i, j, done; + /* + * Frame buffer operations + */ - DTRACE; +static struct fb_ops pm3fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = pm3fb_check_var, + .fb_set_par = pm3fb_set_par, + .fb_setcolreg = pm3fb_setcolreg, + .fb_pan_display = pm3fb_pan_display, + .fb_fillrect = cfb_fillrect, /* Needed !!! */ + .fb_copyarea = cfb_copyarea, /* Needed !!! */ + .fb_imageblit = cfb_imageblit, /* Needed !!! */ + .fb_blank = pm3fb_blank, +}; - for (i = 0; i < PM3_MAX_BOARD; i++) { - dev_array[i] = NULL; - fb_info[i].dev = NULL; - } +/* ------------------------------------------------------------------------- */ - dev = pci_get_device(PCI_VENDOR_ID_3DLABS, - PCI_DEVICE_ID_3DLABS_PERMEDIA3, dev); + /* + * Initialization + */ - for (i = 0; ((i < PM3_MAX_BOARD) && dev); i++) { - dev_array[i] = dev; - dev = pci_get_device(PCI_VENDOR_ID_3DLABS, - PCI_DEVICE_ID_3DLABS_PERMEDIA3, dev); - } +/* mmio register are already mapped when this function is called */ +/* the pm3fb_fix.smem_start is also set */ +static unsigned long pm3fb_size_memory(struct pm3_par *par) +{ + unsigned long memsize = 0, tempBypass, i, temp1, temp2; + unsigned char __iomem *screen_mem; - if (dev) { /* more than PM3_MAX_BOARD */ - printk(KERN_WARNING "pm3fb: Warning: more than %d boards found\n", - PM3_MAX_BOARD); + pm3fb_fix.smem_len = 64 * 1024 * 1024; /* request full aperture size */ + /* Linear frame buffer - request region and map it. */ + if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len, + "pm3fb smem")) { + printk(KERN_WARNING "pm3fb: Can't reserve smem.\n"); + return 0; } - - if (!dev_array[0]) { /* not a single board, abort */ - return; + screen_mem = + ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + if (!screen_mem) { + printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n"); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + return 0; } - /* allocate user-defined boards */ - for (i = 0; i < PM3_MAX_BOARD; i++) { - if ((bus[i] >= 0) && (slot[i] >= 0) && (func[i] >= 0)) { - for (j = 0; j < PM3_MAX_BOARD; j++) { - if ((dev_array[j] != NULL) && - (dev_array[j]->bus->number == bus[i]) - && (PCI_SLOT(dev_array[j]->devfn) == - slot[i]) - && (PCI_FUNC(dev_array[j]->devfn) == - func[i])) { - fb_info[i].dev = dev_array[j]; - dev_array[j] = NULL; - } - } - } + /* TODO: card-specific stuff, *before* accessing *any* FB memory */ + /* For Appian Jeronimo 2000 board second head */ + + tempBypass = PM3_READ_REG(par, PM3MemBypassWriteMask); + + DPRINTK("PM3MemBypassWriteMask was: 0x%08lx\n", tempBypass); + + PM3_SLOW_WRITE_REG(par, PM3MemBypassWriteMask, 0xFFFFFFFF); + + /* pm3 split up memory, replicates, and do a lot of nasty stuff IMHO ;-) */ + for (i = 0; i < 32; i++) { + fb_writel(i * 0x00345678, + (screen_mem + (i * 1048576))); + mb(); + temp1 = fb_readl((screen_mem + (i * 1048576))); + + /* Let's check for wrapover, write will fail at 16MB boundary */ + if (temp1 == (i * 0x00345678)) + memsize = i; + else + break; } - /* allocate remaining boards */ - for (i = 0; i < PM3_MAX_BOARD; i++) { - if (fb_info[i].dev == NULL) { - done = 0; - for (j = 0; ((j < PM3_MAX_BOARD) && (!done)); j++) { - if (dev_array[j] != NULL) { - fb_info[i].dev = dev_array[j]; - dev_array[j] = NULL; - done = 1; - } - } + + DPRINTK("First detect pass already got %ld MB\n", memsize + 1); + + if (memsize + 1 == i) { + for (i = 0; i < 32; i++) { + /* Clear first 32MB ; 0 is 0, no need to byteswap */ + writel(0x0000000, + (screen_mem + (i * 1048576))); + mb(); } - } - /* at that point, all PCI Permedia3 are detected and allocated */ - /* now, initialize... or not */ - for (i = 0; i < PM3_MAX_BOARD; i++) { - l_fb_info = &(fb_info[i]); - if (l_fb_info->dev && !disable[i]) { /* PCI device was found and not disabled by user */ - DPRINTK(2, - "found @%lx Vendor %lx Device %lx ; base @ : %lx - %lx - %lx - %lx - %lx - %lx, irq %ld\n", - (unsigned long) l_fb_info->dev, - (unsigned long) l_fb_info->dev->vendor, - (unsigned long) l_fb_info->dev->device, - (unsigned long) - pci_resource_start(l_fb_info->dev, 0), - (unsigned long) - pci_resource_start(l_fb_info->dev, 1), - (unsigned long) - pci_resource_start(l_fb_info->dev, 2), - (unsigned long) - pci_resource_start(l_fb_info->dev, 3), - (unsigned long) - pci_resource_start(l_fb_info->dev, 4), - (unsigned long) - pci_resource_start(l_fb_info->dev, 5), - (unsigned long) l_fb_info->dev->irq); - - l_fb_info->pIOBase = - (unsigned char *) - pci_resource_start(l_fb_info->dev, 0); -#ifdef __BIG_ENDIAN - l_fb_info->pIOBase += PM3_REGS_SIZE; -#endif - l_fb_info->vIOBase = (unsigned char *) -1; - l_fb_info->p_fb = - (unsigned char *) - pci_resource_start(l_fb_info->dev, 1); - l_fb_info->v_fb = (unsigned char *) -1; - - if (!request_mem_region - ((unsigned long)l_fb_info->p_fb, 64 * 1024 * 1024, /* request full aperture size */ - "pm3fb")) { - printk - (KERN_ERR "pm3fb: Error: couldn't request framebuffer memory, board #%ld\n", - l_fb_info->board_num); - continue; - } - if (!request_mem_region - ((unsigned long)l_fb_info->pIOBase, PM3_REGS_SIZE, - "pm3fb I/O regs")) { - printk - (KERN_ERR "pm3fb: Error: couldn't request IObase memory, board #%ld\n", - l_fb_info->board_num); - continue; - } - if (forcesize[l_fb_info->board_num]) - l_fb_info->fb_size = forcesize[l_fb_info->board_num]; - - l_fb_info->fb_size = - pm3fb_size_memory(l_fb_info); - if (l_fb_info->fb_size) { - (void) pci_enable_device(l_fb_info->dev); - pm3fb_common_init(l_fb_info); - } else - printk(KERN_ERR "pm3fb: memory problem, not enabling board #%ld\n", l_fb_info->board_num); + for (i = 32; i < 64; i++) { + fb_writel(i * 0x00345678, + (screen_mem + (i * 1048576))); + mb(); + temp1 = + fb_readl((screen_mem + (i * 1048576))); + temp2 = + fb_readl((screen_mem + ((i - 32) * 1048576))); + /* different value, different RAM... */ + if ((temp1 == (i * 0x00345678)) && (temp2 == 0)) + memsize = i; + else + break; } } -} + DPRINTK("Second detect pass got %ld MB\n", memsize + 1); -static int pm3fb_pan_display(const struct fb_var_screeninfo *var, - struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; + PM3_SLOW_WRITE_REG(par, PM3MemBypassWriteMask, tempBypass); - DTRACE; + iounmap(screen_mem); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + memsize = 1048576 * (memsize + 1); - if (!current_par_valid[l_fb_info->board_num]) - return -EINVAL; + DPRINTK("Returning 0x%08lx bytes\n", memsize); - l_fb_info->current_par->base = /* in 128 bits chunk - i.e. AFTER Shiftbpp */ - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - (var->yoffset * l_fb_info->current_par->width) + - var->xoffset); - PM3_SLOW_WRITE_REG(PM3ScreenBase, l_fb_info->current_par->base); - return 0; + return memsize; } -static int pm3fb_ioctl(struct fb_info *info, u_int cmd, u_long arg) +static int __devinit pm3fb_probe(struct pci_dev *dev, + const struct pci_device_id *ent) { - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - u32 cm, i; -#ifdef PM3FB_MASTER_DEBUG - char cc[3]; -#endif /* PM3FB_MASTER_DEBUG */ + struct fb_info *info; + struct pm3_par *par; + struct device* device = &dev->dev; /* for pci drivers */ + int err, retval = -ENXIO; - switch(cmd) - { -#ifdef PM3FB_MASTER_DEBUG - case PM3FBIO_CLEARMEMORY: - if (copy_from_user(&cm, (void *)arg, sizeof(u32))) - return(-EFAULT); - pm3fb_clear_memory(l_fb_info, cm); - return(0); - break; + err = pci_enable_device(dev); + if (err) { + printk(KERN_WARNING "pm3fb: Can't enable PCI dev: %d\n", err); + return err; + } + /* + * Dynamically allocate info and par + */ + info = framebuffer_alloc(sizeof(struct pm3_par), device); - case PM3FBIO_CLEARCMAP: - if (copy_from_user(cc, (void*)arg, 3 * sizeof(char))) - return(-EFAULT); - pm3fb_clear_colormap(l_fb_info, cc[0], cc[1], cc[2]); - return(0); - break; -#endif /* PM3FB_MASTER_DEBUG */ - - case PM3FBIO_RESETCHIP: - cm = 1; - PM3_SLOW_WRITE_REG(PM3ResetStatus, 1); - for (i = 0 ; (i < 10000) && cm ; i++) - { - PM3_DELAY(10); - cm = PM3_READ_REG(PM3ResetStatus); - } - if (cm) - { - printk(KERN_ERR "pm3fb: chip reset failed with status 0x%x\n", cm); - return(-EIO); - } - /* first thing first, reload memory timings */ - pm3fb_write_memory_timings(l_fb_info); -#ifdef PM3FB_USE_ACCEL - pm3fb_init_engine(l_fb_info); -#endif /* PM3FB_USE_ACCEL */ - pm3fb_write_mode(l_fb_info); - return(0); - break; + if (!info) + return -ENOMEM; + par = info->par; - default: - DPRINTK(2, "unknown ioctl: %d (%x)\n", cmd, cmd); - return(-EINVAL); + /* + * Here we set the screen_base to the virtual memory address + * for the framebuffer. + */ + pm3fb_fix.mmio_start = pci_resource_start(dev, 0); + pm3fb_fix.mmio_len = PM3_REGS_SIZE; + + /* Registers - request region and map it. */ + if (!request_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len, + "pm3fb regbase")) { + printk(KERN_WARNING "pm3fb: Can't reserve regbase.\n"); + goto err_exit_neither; + } + par->v_regs = + ioremap_nocache(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len); + if (!par->v_regs) { + printk(KERN_WARNING "pm3fb: Can't remap %s register area.\n", + pm3fb_fix.id); + release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len); + goto err_exit_neither; + } + +#if defined(__BIG_ENDIAN) + pm3fb_fix.mmio_start += PM3_REGS_SIZE; + DPRINTK("Adjusting register base for big-endian.\n"); +#endif + /* Linear frame buffer - request region and map it. */ + pm3fb_fix.smem_start = pci_resource_start(dev, 1); + pm3fb_fix.smem_len = pm3fb_size_memory(par); + if (!pm3fb_fix.smem_len) + { + printk(KERN_WARNING "pm3fb: Can't find memory on board.\n"); + goto err_exit_mmio; } -} - -/* ****************************************** */ -/* ***** standard FB API init functions ***** */ -/* ****************************************** */ + if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len, + "pm3fb smem")) { + printk(KERN_WARNING "pm3fb: Can't reserve smem.\n"); + goto err_exit_mmio; + } + info->screen_base = + ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + if (!info->screen_base) { + printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n"); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + goto err_exit_mmio; + } + info->screen_size = pm3fb_fix.smem_len; -int __init pm3fb_setup(char *options) -{ - long opsi = strlen(options); + info->fbops = &pm3fb_ops; - DTRACE; + par->video = PM3_READ_REG(par, PM3VideoControl); - memcpy(g_options, options, - ((opsi + 1) > - PM3_OPTIONS_SIZE) ? PM3_OPTIONS_SIZE : (opsi + 1)); - g_options[PM3_OPTIONS_SIZE - 1] = 0; + info->fix = pm3fb_fix; + info->pseudo_palette = par->palette; + info->flags = FBINFO_DEFAULT;/* | FBINFO_HWACCEL_YPAN;*/ - return (0); -} + /* + * This should give a reasonable default video mode. The following is + * done when we can set a video mode. + */ + if (!mode_option) + mode_option = "640x480@60"; -int __init pm3fb_init(void) -{ - DTRACE; + retval = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8); - DPRINTK(2, "This is pm3fb.c, CVS version: $Header: /cvsroot/linux/drivers/video/pm3fb.c,v 1.1 2002/02/25 19:11:06 marcelo Exp $"); + if (!retval || retval == 4) { + retval = -EINVAL; + goto err_exit_both; + } - pm3fb_real_setup(g_options); + /* This has to been done !!! */ + if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { + retval = -ENOMEM; + goto err_exit_both; + } - pm3fb_detect(); + /* + * For drivers that can... + */ + pm3fb_check_var(&info->var, info); - if (!fb_info[0].dev) { /* not even one board ??? */ - DPRINTK(1, "No PCI Permedia3 board detected\n"); + if (register_framebuffer(info) < 0) { + retval = -EINVAL; + goto err_exit_all; } - return (0); + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + pci_set_drvdata(dev, info); /* or dev_set_drvdata(device, info) */ + return 0; + + err_exit_all: + fb_dealloc_cmap(&info->cmap); + err_exit_both: + iounmap(info->screen_base); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + err_exit_mmio: + iounmap(par->v_regs); + release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len); + err_exit_neither: + framebuffer_release(info); + return retval; } -/* ************************* */ -/* **** Module support ***** */ -/* ************************* */ + /* + * Cleanup + */ +static void __devexit pm3fb_remove(struct pci_dev *dev) +{ + struct fb_info *info = pci_get_drvdata(dev); -#ifdef MODULE -MODULE_AUTHOR("Romain Dolbeau"); -MODULE_DESCRIPTION("Permedia3 framebuffer device driver"); -static char *mode[PM3_MAX_BOARD]; -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode,"video mode"); -module_param_array(disable, short, NULL, 0); -MODULE_PARM_DESC(disable,"disable board"); -static short off[PM3_MAX_BOARD]; -module_param_array(off, short, NULL, 0); -MODULE_PARM_DESC(off,"disable board"); -static char *pciid[PM3_MAX_BOARD]; -module_param_array(pciid, charp, NULL, 0); -MODULE_PARM_DESC(pciid,"board PCI Id"); -module_param_array(noaccel, short, NULL, 0); -MODULE_PARM_DESC(noaccel,"disable accel"); -static char *font[PM3_MAX_BOARD]; -module_param_array(font, charp, NULL, 0); -MODULE_PARM_DESC(font,"choose font"); -module_param(depth, short, NULL, 0); -MODULE_PARM_DESC(depth,"boot-time depth"); -module_param(printtimings, short, NULL, 0); -MODULE_PARM_DESC(printtimings, "print the memory timings of the card(s)"); -module_param(forcesize, short, NULL, 0); -MODULE_PARM_DESC(forcesize, "force specified memory size"); -/* -MODULE_SUPPORTED_DEVICE("Permedia3 PCI boards") -MODULE_GENERIC_TABLE(gtype,name) -MODULE_DEVICE_TABLE(type,name) -*/ + if (info) { + struct fb_fix_screeninfo *fix = &info->fix; + struct pm3_par *par = info->par; -void pm3fb_build_options(void) -{ - int i; - char ts[128]; + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); - strcpy(g_options, "pm3fb"); - for (i = 0; i < PM3_MAX_BOARD ; i++) - { - if (mode[i]) - { - sprintf(ts, ",mode:%d:%s", i, mode[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (disable[i] || off[i]) - { - sprintf(ts, ",disable:%d:", i); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (pciid[i]) - { - sprintf(ts, ",pciid:%d:%s", i, pciid[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (noaccel[i]) - { - sprintf(ts, ",noaccel:%d:", i); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (font[i]) - { - sprintf(ts, ",font:%d:%s", i, font[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (depth[i]) - { - sprintf(ts, ",depth:%d:%d", i, depth[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } + iounmap(info->screen_base); + release_mem_region(fix->smem_start, fix->smem_len); + iounmap(par->v_regs); + release_mem_region(fix->mmio_start, fix->mmio_len); + + pci_set_drvdata(dev, NULL); + framebuffer_release(info); } - g_options[PM3_OPTIONS_SIZE - 1] = '\0'; - DPRINTK(1, "pm3fb use options: %s\n", g_options); } -int init_module(void) +static struct pci_device_id pm3fb_id_table[] = { + { PCI_VENDOR_ID_3DLABS, 0x0a, + PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, + 0xff0000, 0 }, + { 0, } +}; + +/* For PCI drivers */ +static struct pci_driver pm3fb_driver = { + .name = "pm3fb", + .id_table = pm3fb_id_table, + .probe = pm3fb_probe, + .remove = __devexit_p(pm3fb_remove), +}; + +MODULE_DEVICE_TABLE(pci, pm3fb_id_table); + +int __init pm3fb_init(void) { - DTRACE; + /* + * For kernel boot options (in 'video=pm3fb:<options>' format) + */ +#ifndef MODULE + char *option = NULL; - pm3fb_build_options(); + if (fb_get_options("pm3fb", &option)) + return -ENODEV; + pm3fb_setup(option); +#endif - pm3fb_init(); + return pci_register_driver(&pm3fb_driver); +} - return 0; +static void __exit pm3fb_exit(void) +{ + pci_unregister_driver(&pm3fb_driver); } -void cleanup_module(void) +#ifdef MODULE + /* + * Setup + */ + +/* + * Only necessary if your driver takes special options, + * otherwise we fall back on the generic fb_setup(). + */ +int __init pm3fb_setup(char *options) { - DTRACE; - { - unsigned long i; - struct pm3fb_info *l_fb_info; - for (i = 0; i < PM3_MAX_BOARD; i++) { - l_fb_info = &(fb_info[i]); - pci_dev_put(l_fb_info->dev); - if (l_fb_info->dev != NULL && !(disable[l_fb_info->board_num])) { - if (l_fb_info->vIOBase != (unsigned char *) -1) { - pm3fb_unmapIO(l_fb_info); - release_mem_region(l_fb_info->p_fb, - l_fb_info->fb_size); - release_mem_region(l_fb_info->pIOBase, - PM3_REGS_SIZE); - } - unregister_framebuffer(&l_fb_info->gen.info); - } - } - } + /* Parse user speficied options (`video=pm3fb:') */ + return 0; } #endif /* MODULE */ + +module_init(pm3fb_init); +module_exit(pm3fb_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/video/riva/rivafb-i2c.c b/drivers/video/riva/rivafb-i2c.c index 76e6ce353c8e..a0e22ac483a3 100644 --- a/drivers/video/riva/rivafb-i2c.c +++ b/drivers/video/riva/rivafb-i2c.c @@ -70,8 +70,6 @@ static int riva_gpio_getscl(void* data) if (VGA_RD08(par->riva.PCIO, 0x3d5) & 0x04) val = 1; - val = VGA_RD08(par->riva.PCIO, 0x3d5); - return val; } diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig index c287a9ae4fdd..ca75b3ad3a2e 100644 --- a/drivers/w1/Kconfig +++ b/drivers/w1/Kconfig @@ -1,4 +1,5 @@ menu "Dallas's 1-wire bus" + depends on HAS_IOMEM config W1 tristate "Dallas's 1-wire support" |