diff options
Diffstat (limited to 'drivers')
226 files changed, 14339 insertions, 4253 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 88681aca88c..3f3489c5ca8 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -9,7 +9,6 @@ menuconfig ACPI depends on PCI depends on PM select PNP - select CPU_IDLE default y help Advanced Configuration and Power Interface (ACPI) support for @@ -66,7 +65,6 @@ config ACPI_PROCFS config ACPI_PROCFS_POWER bool "Deprecated power /proc/acpi directories" depends on PROC_FS - default y help For backwards compatibility, this option allows deprecated power /proc/acpi/ directories to exist, even when @@ -90,13 +88,6 @@ config ACPI_POWER_METER To compile this driver as a module, choose M here: the module will be called power-meter. -config ACPI_SYSFS_POWER - bool "Future power /sys interface" - select POWER_SUPPLY - default y - help - Say N to disable power /sys interface - config ACPI_EC_DEBUGFS tristate "EC read/write access through /sys/kernel/debug/ec" default n @@ -136,6 +127,7 @@ config ACPI_PROC_EVENT config ACPI_AC tristate "AC Adapter" depends on X86 + select POWER_SUPPLY default y help This driver supports the AC Adapter object, which indicates @@ -148,6 +140,7 @@ config ACPI_AC config ACPI_BATTERY tristate "Battery" depends on X86 + select POWER_SUPPLY default y help This driver adds support for battery information through @@ -206,6 +199,7 @@ config ACPI_DOCK config ACPI_PROCESSOR tristate "Processor" select THERMAL + select CPU_IDLE default y help This driver installs ACPI as the idle handler for Linux and uses @@ -364,6 +358,7 @@ config ACPI_HOTPLUG_MEMORY config ACPI_SBS tristate "Smart Battery System" depends on X86 + select POWER_SUPPLY help This driver supports the Smart Battery System, another type of access to battery information, found on some laptops. diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 56205a0b85d..ba9afeaa23a 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -32,9 +32,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #endif -#ifdef CONFIG_ACPI_SYSFS_POWER #include <linux/power_supply.h> -#endif #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -86,9 +84,7 @@ static struct acpi_driver acpi_ac_driver = { }; struct acpi_ac { -#ifdef CONFIG_ACPI_SYSFS_POWER struct power_supply charger; -#endif struct acpi_device * device; unsigned long long state; }; @@ -104,7 +100,6 @@ static const struct file_operations acpi_ac_fops = { .release = single_release, }; #endif -#ifdef CONFIG_ACPI_SYSFS_POWER static int get_ac_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -123,7 +118,6 @@ static int get_ac_property(struct power_supply *psy, static enum power_supply_property ac_props[] = { POWER_SUPPLY_PROP_ONLINE, }; -#endif /* -------------------------------------------------------------------------- AC Adapter Management -------------------------------------------------------------------------- */ @@ -247,9 +241,7 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event) dev_name(&device->dev), event, (u32) ac->state); acpi_notifier_call_chain(device, event, (u32) ac->state); -#ifdef CONFIG_ACPI_SYSFS_POWER kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); -#endif } return; @@ -282,14 +274,12 @@ static int acpi_ac_add(struct acpi_device *device) #endif if (result) goto end; -#ifdef CONFIG_ACPI_SYSFS_POWER ac->charger.name = acpi_device_bid(device); ac->charger.type = POWER_SUPPLY_TYPE_MAINS; ac->charger.properties = ac_props; ac->charger.num_properties = ARRAY_SIZE(ac_props); ac->charger.get_property = get_ac_property; power_supply_register(&ac->device->dev, &ac->charger); -#endif printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device), acpi_device_bid(device), @@ -316,10 +306,8 @@ static int acpi_ac_resume(struct acpi_device *device) old_state = ac->state; if (acpi_ac_get_state(ac)) return 0; -#ifdef CONFIG_ACPI_SYSFS_POWER if (old_state != ac->state) kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); -#endif return 0; } @@ -333,10 +321,8 @@ static int acpi_ac_remove(struct acpi_device *device, int type) ac = acpi_driver_data(device); -#ifdef CONFIG_ACPI_SYSFS_POWER if (ac->charger.dev) power_supply_unregister(&ac->charger); -#endif #ifdef CONFIG_ACPI_PROCFS_POWER acpi_ac_remove_fs(device); #endif diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index d93cc06f4bf..a7e1d1aa410 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -21,7 +21,7 @@ acpi-y += exconfig.o exfield.o exnames.o exoparg6.o exresolv.o exstorob.o\ excreate.o exmisc.o exoparg2.o exregion.o exstore.o exutils.o \ exdump.o exmutex.o exoparg3.o exresnte.o exstoren.o exdebug.o -acpi-y += hwacpi.o hwgpe.o hwregs.o hwsleep.o hwxface.o hwvalid.o +acpi-y += hwacpi.o hwgpe.o hwregs.o hwsleep.o hwxface.o hwvalid.o hwpci.o acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o @@ -44,4 +44,5 @@ acpi-y += tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o acpi-y += utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \ utcopy.o utdelete.o utglobal.o utmath.o utobject.o \ - utstate.o utmutex.o utobject.o utresrc.o utlock.o utids.o + utstate.o utmutex.o utobject.o utresrc.o utlock.o utids.o \ + utosi.o utxferror.o diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h index 48faf3eba9f..72e9d5eb083 100644 --- a/drivers/acpi/acpica/acdebug.h +++ b/drivers/acpi/acpica/acdebug.h @@ -105,6 +105,8 @@ void acpi_db_set_method_data(char *type_arg, char *index_arg, char *value_arg); acpi_status acpi_db_display_objects(char *obj_type_arg, char *display_count_arg); +void acpi_db_display_interfaces(char *action_arg, char *interface_name_arg); + acpi_status acpi_db_find_name_in_namespace(char *name_arg); void acpi_db_set_scope(char *name); diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index 36867cd70ea..a6f99cc37a1 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -105,8 +105,9 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, struct acpi_gpe_block_info **return_gpe_block); acpi_status -acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, - struct acpi_gpe_block_info *gpe_block); +acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *ignored); acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block); diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 1d192142c69..ad88fcae4eb 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -132,6 +132,7 @@ struct acpi_table_fadt acpi_gbl_FADT; u32 acpi_current_gpe_count; u32 acpi_gbl_trace_flags; acpi_name acpi_gbl_trace_method_name; +u8 acpi_gbl_system_awake_and_running; #endif @@ -187,6 +188,10 @@ ACPI_EXTERN u8 acpi_gbl_integer_bit_width; ACPI_EXTERN u8 acpi_gbl_integer_byte_width; ACPI_EXTERN u8 acpi_gbl_integer_nybble_width; +/* Mutex for _OSI support */ + +ACPI_EXTERN acpi_mutex acpi_gbl_osi_mutex; + /* Reader/Writer lock is used for namespace walk and dynamic table unload */ ACPI_EXTERN struct acpi_rw_lock acpi_gbl_namespace_rw_lock; @@ -255,6 +260,7 @@ ACPI_EXTERN acpi_init_handler acpi_gbl_init_handler; ACPI_EXTERN acpi_tbl_handler acpi_gbl_table_handler; ACPI_EXTERN void *acpi_gbl_table_handler_context; ACPI_EXTERN struct acpi_walk_state *acpi_gbl_breakpoint_walk; +ACPI_EXTERN acpi_interface_handler acpi_gbl_interface_handler; /* Owner ID support */ @@ -273,8 +279,8 @@ ACPI_EXTERN u8 acpi_gbl_debugger_configuration; ACPI_EXTERN u8 acpi_gbl_step_to_next_call; ACPI_EXTERN u8 acpi_gbl_acpi_hardware_present; ACPI_EXTERN u8 acpi_gbl_events_initialized; -ACPI_EXTERN u8 acpi_gbl_system_awake_and_running; ACPI_EXTERN u8 acpi_gbl_osi_data; +ACPI_EXTERN struct acpi_interface_info *acpi_gbl_supported_interfaces; #ifndef DEFINE_ACPI_GLOBALS @@ -364,6 +370,7 @@ ACPI_EXTERN struct acpi_fixed_event_handler ACPI_EXTERN struct acpi_gpe_xrupt_info *acpi_gbl_gpe_xrupt_list_head; ACPI_EXTERN struct acpi_gpe_block_info *acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS]; +ACPI_EXTERN u8 acpi_all_gpes_initialized; /***************************************************************************** * diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 120b3af5659..167470ad2d2 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -121,6 +121,13 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, struct acpi_gpe_block_info *gpe_block, void *context); +/* + * hwpci - PCI configuration support + */ +acpi_status +acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id, + acpi_handle root_pci_device, acpi_handle pci_region); + #ifdef ACPI_FUTURE_USAGE /* * hwtimer - ACPI Timer prototypes diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 7dad9160f20..2ceb0c05b2d 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -413,6 +413,7 @@ struct acpi_handler_info { void *context; /* Context to be passed to handler */ struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */ u8 orig_flags; /* Original misc info about this GPE */ + u8 orig_enabled; /* Set if the GPE was originally enabled */ }; union acpi_gpe_dispatch_info { @@ -457,6 +458,7 @@ struct acpi_gpe_block_info { u32 register_count; /* Number of register pairs in block */ u16 gpe_count; /* Number of individual GPEs in block */ u8 block_base_number; /* Base GPE number for this block */ + u8 initialized; /* If set, the GPE block has been initialized */ }; /* Information about GPE interrupt handlers, one per each interrupt level used for GPEs */ @@ -473,7 +475,6 @@ struct acpi_gpe_walk_info { struct acpi_gpe_block_info *gpe_block; u16 count; acpi_owner_id owner_id; - u8 enable_this_gpe; u8 execute_by_owner_id; }; @@ -854,7 +855,7 @@ struct acpi_bit_register_info { ACPI_BITMASK_POWER_BUTTON_STATUS | \ ACPI_BITMASK_SLEEP_BUTTON_STATUS | \ ACPI_BITMASK_RT_CLOCK_STATUS | \ - ACPI_BITMASK_PCIEXP_WAKE_DISABLE | \ + ACPI_BITMASK_PCIEXP_WAKE_STATUS | \ ACPI_BITMASK_WAKE_STATUS) #define ACPI_BITMASK_TIMER_ENABLE 0x0001 @@ -909,15 +910,21 @@ struct acpi_bit_register_info { #define ACPI_OSI_WIN_VISTA 0x07 #define ACPI_OSI_WINSRV_2008 0x08 #define ACPI_OSI_WIN_VISTA_SP1 0x09 -#define ACPI_OSI_WIN_7 0x0A +#define ACPI_OSI_WIN_VISTA_SP2 0x0A +#define ACPI_OSI_WIN_7 0x0B #define ACPI_ALWAYS_ILLEGAL 0x00 struct acpi_interface_info { char *name; + struct acpi_interface_info *next; + u8 flags; u8 value; }; +#define ACPI_OSI_INVALID 0x01 +#define ACPI_OSI_DYNAMIC 0x02 + struct acpi_port_info { char *name; u16 start; @@ -997,7 +1004,7 @@ struct acpi_port_info { struct acpi_db_method_info { acpi_handle main_thread_gate; acpi_handle thread_complete_gate; - u32 *threads; + acpi_thread_id *threads; u32 num_threads; u32 num_created; u32 num_completed; diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h index 9894929a2ab..8d5c9e0a495 100644 --- a/drivers/acpi/acpica/acmacros.h +++ b/drivers/acpi/acpica/acmacros.h @@ -338,8 +338,8 @@ * the plist contains a set of parens to allow variable-length lists. * These macros are used for both the debug and non-debug versions of the code. */ -#define ACPI_ERROR_NAMESPACE(s, e) acpi_ns_report_error (AE_INFO, s, e); -#define ACPI_ERROR_METHOD(s, n, p, e) acpi_ns_report_method_error (AE_INFO, s, n, p, e); +#define ACPI_ERROR_NAMESPACE(s, e) acpi_ut_namespace_error (AE_INFO, s, e); +#define ACPI_ERROR_METHOD(s, n, p, e) acpi_ut_method_error (AE_INFO, s, n, p, e); #define ACPI_WARN_PREDEFINED(plist) acpi_ut_predefined_warning plist #define ACPI_INFO_PREDEFINED(plist) acpi_ut_predefined_info plist diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index 9f60ff00220..d44d3bc5b84 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -339,18 +339,6 @@ acpi_object_type acpi_ns_get_type(struct acpi_namespace_node *node); u32 acpi_ns_local(acpi_object_type type); void -acpi_ns_report_error(const char *module_name, - u32 line_number, - const char *internal_name, acpi_status lookup_status); - -void -acpi_ns_report_method_error(const char *module_name, - u32 line_number, - const char *message, - struct acpi_namespace_node *node, - const char *path, acpi_status lookup_status); - -void acpi_ns_print_node_pathname(struct acpi_namespace_node *node, const char *msg); acpi_status acpi_ns_build_internal_name(struct acpi_namestring_info *info); diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index 54857fa87aa..bdbfaf22bd1 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -248,7 +248,7 @@ ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO}; u32 base_byte_offset; /* Byte offset within containing object */\ u32 value; /* Value to store into the Bank or Index register */\ u8 start_field_bit_offset;/* Bit offset within first field datum (0-63) */\ - u8 access_bit_width; /* Read/Write size in bits (8-64) */ + struct acpi_object_field_common { /* COMMON FIELD (for BUFFER, REGION, BANK, and INDEX fields) */ ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Parent Operation Region object (REGION/BANK fields only) */ diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 35df755251c..72e4183c193 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -312,8 +312,6 @@ void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list); /* * uteval - object evaluation */ -acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state); - acpi_status acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, char *path, @@ -395,6 +393,21 @@ acpi_status acpi_ut_get_object_size(union acpi_operand_object *obj, acpi_size * obj_length); /* + * utosi - Support for the _OSI predefined control method + */ +acpi_status acpi_ut_initialize_interfaces(void); + +void acpi_ut_interface_terminate(void); + +acpi_status acpi_ut_install_interface(acpi_string interface_name); + +acpi_status acpi_ut_remove_interface(acpi_string interface_name); + +struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name); + +acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state); + +/* * utstate - Generic state creation/cache routines */ void @@ -473,17 +486,6 @@ u8 acpi_ut_valid_acpi_char(char character, u32 position); acpi_status acpi_ut_strtoul64(char *string, u32 base, u64 * ret_integer); -void ACPI_INTERNAL_VAR_XFACE -acpi_ut_predefined_warning(const char *module_name, - u32 line_number, - char *pathname, - u8 node_flags, const char *format, ...); - -void ACPI_INTERNAL_VAR_XFACE -acpi_ut_predefined_info(const char *module_name, - u32 line_number, - char *pathname, u8 node_flags, const char *format, ...); - /* Values for Base above (16=Hex, 10=Decimal) */ #define ACPI_ANY_BASE 0 @@ -574,6 +576,32 @@ acpi_status acpi_ut_create_list(char *list_name, u16 object_size, struct acpi_memory_list **return_cache); -#endif +#endif /* ACPI_DBG_TRACK_ALLOCATIONS */ + +/* + * utxferror - various error/warning output functions + */ +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_warning(const char *module_name, + u32 line_number, + char *pathname, + u8 node_flags, const char *format, ...); + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_info(const char *module_name, + u32 line_number, + char *pathname, u8 node_flags, const char *format, ...); + +void +acpi_ut_namespace_error(const char *module_name, + u32 line_number, + const char *internal_name, acpi_status lookup_status); + +void +acpi_ut_method_error(const char *module_name, + u32 line_number, + const char *message, + struct acpi_namespace_node *node, + const char *path, acpi_status lookup_status); #endif /* _ACUTILS_H */ diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 64750ee96e2..d94dd8974b5 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -573,7 +573,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.thread_id = NULL; + method_desc->method.mutex->mutex.thread_id = 0; } } diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c index d555b374e31..6b0b5d08d97 100644 --- a/drivers/acpi/acpica/dswexec.c +++ b/drivers/acpi/acpica/dswexec.c @@ -300,10 +300,25 @@ acpi_ds_exec_begin_op(struct acpi_walk_state *walk_state, * we must enter this object into the namespace. The created * object is temporary and will be deleted upon completion of * the execution of this method. + * + * Note 10/2010: Except for the Scope() op. This opcode does + * not actually create a new object, it refers to an existing + * object. However, for Scope(), we want to indeed open a + * new scope. */ - status = acpi_ds_load2_begin_op(walk_state, NULL); + if (op->common.aml_opcode != AML_SCOPE_OP) { + status = + acpi_ds_load2_begin_op(walk_state, NULL); + } else { + status = + acpi_ds_scope_stack_push(op->named.node, + op->named.node-> + type, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } } - break; case AML_CLASS_EXECUTE: diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c index 303618889da..c61c3039c31 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -95,47 +95,6 @@ acpi_status acpi_ev_initialize_events(void) /******************************************************************************* * - * FUNCTION: acpi_ev_install_fadt_gpes - * - * PARAMETERS: None - * - * RETURN: Status - * - * DESCRIPTION: Completes initialization of the FADT-defined GPE blocks - * (0 and 1). The HW must be fully initialized at this point, - * including global lock support. - * - ******************************************************************************/ - -acpi_status acpi_ev_install_fadt_gpes(void) -{ - acpi_status status; - - ACPI_FUNCTION_TRACE(ev_install_fadt_gpes); - - /* Namespace must be locked */ - - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - return (status); - } - - /* FADT GPE Block 0 */ - - (void)acpi_ev_initialize_gpe_block(acpi_gbl_fadt_gpe_device, - acpi_gbl_gpe_fadt_blocks[0]); - - /* FADT GPE Block 1 */ - - (void)acpi_ev_initialize_gpe_block(acpi_gbl_fadt_gpe_device, - acpi_gbl_gpe_fadt_blocks[1]); - - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - return_ACPI_STATUS(AE_OK); -} - -/******************************************************************************* - * * FUNCTION: acpi_ev_install_xrupt_handlers * * PARAMETERS: None diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index 85445fb5844..020add3eee1 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -363,6 +363,7 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, gpe_block->gpe_count = (u16)(register_count * ACPI_GPE_REGISTER_WIDTH); gpe_block->register_count = register_count; gpe_block->block_base_number = gpe_block_base_number; + gpe_block->initialized = FALSE; ACPI_MEMCPY(&gpe_block->block_address, gpe_block_address, sizeof(struct acpi_generic_address)); @@ -385,11 +386,12 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, return_ACPI_STATUS(status); } + acpi_all_gpes_initialized = FALSE; + /* Find all GPE methods (_Lxx or_Exx) for this block */ walk_info.gpe_block = gpe_block; walk_info.gpe_device = gpe_device; - walk_info.enable_this_gpe = FALSE; walk_info.execute_by_owner_id = FALSE; status = acpi_ns_walk_namespace(ACPI_TYPE_METHOD, gpe_device, @@ -434,35 +436,34 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, ******************************************************************************/ acpi_status -acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, - struct acpi_gpe_block_info *gpe_block) +acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *ignored) { acpi_status status; struct acpi_gpe_event_info *gpe_event_info; u32 gpe_enabled_count; u32 gpe_index; - u32 gpe_number; u32 i; u32 j; ACPI_FUNCTION_TRACE(ev_initialize_gpe_block); - /* Ignore a null GPE block (e.g., if no GPE block 1 exists) */ - - if (!gpe_block) { + /* + * Ignore a null GPE block (e.g., if no GPE block 1 exists) and + * GPE blocks that have been initialized already. + */ + if (!gpe_block || gpe_block->initialized) { return_ACPI_STATUS(AE_OK); } /* - * Enable all GPEs that have a corresponding method. Any other GPEs - * within this block must be enabled via the acpi_enable_gpe interface. + * Enable all GPEs that have a corresponding method and have the + * ACPI_GPE_CAN_WAKE flag unset. Any other GPEs within this block must + * be enabled via the acpi_enable_gpe() interface. */ gpe_enabled_count = 0; - if (gpe_device == acpi_gbl_fadt_gpe_device) { - gpe_device = NULL; - } - for (i = 0; i < gpe_block->register_count; i++) { for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { @@ -470,27 +471,19 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, gpe_index = (i * ACPI_GPE_REGISTER_WIDTH) + j; gpe_event_info = &gpe_block->event_info[gpe_index]; - gpe_number = gpe_index + gpe_block->block_base_number; /* Ignore GPEs that have no corresponding _Lxx/_Exx method */ - if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD)) { + if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD) + || (gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { continue; } - /* - * If the GPE has already been enabled for runtime - * signaling, make sure it remains enabled, but do not - * increment its reference counter. - */ - status = gpe_event_info->runtime_count ? - acpi_ev_enable_gpe(gpe_event_info) : - acpi_enable_gpe(gpe_device, gpe_number); - + status = acpi_raw_enable_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Could not enable GPE 0x%02X", - gpe_number)); + "Could not enable GPE 0x%02X", + gpe_index + gpe_block->block_base_number)); continue; } @@ -504,5 +497,7 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, gpe_enabled_count)); } + gpe_block->initialized = TRUE; + return_ACPI_STATUS(AE_OK); } diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index 3084c5de1bb..2c7def95f72 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -210,8 +210,7 @@ acpi_status acpi_ev_gpe_initialize(void) * * DESCRIPTION: Check for new GPE methods (_Lxx/_Exx) made available as a * result of a Load() or load_table() operation. If new GPE - * methods have been installed, register the new methods and - * enable and runtime GPEs that are associated with them. + * methods have been installed, register the new methods. * ******************************************************************************/ @@ -239,7 +238,6 @@ void acpi_ev_update_gpes(acpi_owner_id table_owner_id) walk_info.owner_id = table_owner_id; walk_info.execute_by_owner_id = TRUE; walk_info.count = 0; - walk_info.enable_this_gpe = TRUE; /* Walk the interrupt level descriptor list */ @@ -301,8 +299,6 @@ void acpi_ev_update_gpes(acpi_owner_id table_owner_id) * * If walk_info->execute_by_owner_id is TRUE, we only execute examine GPE methods * with that owner. - * If walk_info->enable_this_gpe is TRUE, the GPE that is referred to by a GPE - * method is immediately enabled (Used for Load/load_table operators) * ******************************************************************************/ @@ -315,8 +311,6 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, struct acpi_gpe_walk_info *walk_info = ACPI_CAST_PTR(struct acpi_gpe_walk_info, context); struct acpi_gpe_event_info *gpe_event_info; - struct acpi_namespace_node *gpe_device; - acpi_status status; u32 gpe_number; char name[ACPI_NAME_SIZE + 1]; u8 type; @@ -421,29 +415,6 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, gpe_event_info->flags |= (u8)(type | ACPI_GPE_DISPATCH_METHOD); gpe_event_info->dispatch.method_node = method_node; - /* - * Enable this GPE if requested. This only happens when during the - * execution of a Load or load_table operator. We have found a new - * GPE method and want to immediately enable the GPE if it is a - * runtime GPE. - */ - if (walk_info->enable_this_gpe) { - - walk_info->count++; - gpe_device = walk_info->gpe_device; - - if (gpe_device == acpi_gbl_fadt_gpe_device) { - gpe_device = NULL; - } - - status = acpi_enable_gpe(gpe_device, gpe_number); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Could not enable GPE 0x%02X", - gpe_number)); - } - } - ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "Registered GPE method %s as GPE number 0x%.2X\n", name, gpe_number)); diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index df0aea9a8cf..fcaed9fb44f 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -553,7 +553,7 @@ acpi_status acpi_ev_release_global_lock(void) acpi_gbl_global_lock_acquired = FALSE; /* Release the local GL mutex */ - acpi_ev_global_lock_thread_id = NULL; + acpi_ev_global_lock_thread_id = 0; acpi_ev_global_lock_acquired = 0; acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex); return_ACPI_STATUS(status); diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index f40d271bf56..0b47a6dc929 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -289,8 +289,8 @@ acpi_ev_pci_config_region_setup(acpi_handle handle, } /* - * Get the PCI device and function numbers from the _ADR object contained - * in the parent's scope. + * Get the PCI device and function numbers from the _ADR object + * contained in the parent's scope. */ status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR, pci_device_node, &pci_value); @@ -320,9 +320,15 @@ acpi_ev_pci_config_region_setup(acpi_handle handle, pci_id->bus = ACPI_LOWORD(pci_value); } - /* Complete this device's pci_id */ + /* Complete/update the PCI ID for this device */ - acpi_os_derive_pci_id(pci_root_node, region_obj->region.node, &pci_id); + status = + acpi_hw_derive_pci_id(pci_id, pci_root_node, + region_obj->region.node); + if (ACPI_FAILURE(status)) { + ACPI_FREE(pci_id); + return_ACPI_STATUS(status); + } *region_context = pci_id; return_ACPI_STATUS(AE_OK); diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 14e48add32f..36af222cac6 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -726,15 +726,16 @@ acpi_install_gpe_handler(acpi_handle gpe_device, (ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); /* - * If the GPE is associated with a method and it cannot wake up the - * system from sleep states, it was enabled automatically during - * initialization, so it has to be disabled now to avoid spurious - * execution of the handler. + * If the GPE is associated with a method, it might have been enabled + * automatically during initialization, in which case it has to be + * disabled now to avoid spurious execution of the handler. */ if ((handler->orig_flags & ACPI_GPE_DISPATCH_METHOD) - && !(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) + && gpe_event_info->runtime_count) { + handler->orig_enabled = 1; (void)acpi_raw_disable_gpe(gpe_event_info); + } /* Install the handler */ @@ -837,13 +838,13 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, gpe_event_info->flags |= handler->orig_flags; /* - * If the GPE was previously associated with a method and it cannot wake - * up the system from sleep states, it should be enabled at this point - * to restore the post-initialization configuration. + * If the GPE was previously associated with a method and it was + * enabled, it should be enabled at this point to restore the + * post-initialization configuration. */ if ((handler->orig_flags & ACPI_GPE_DISPATCH_METHOD) - && !(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) + && handler->orig_enabled) (void)acpi_raw_enable_gpe(gpe_event_info); /* Now we can free the handler object */ diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index 304825528d4..a1dabe3fd8a 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -379,21 +379,12 @@ acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number) /* Ensure that we have a valid GPE number */ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); - if (!gpe_event_info) { + if (gpe_event_info) { + gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; + } else { status = AE_BAD_PARAMETER; - goto unlock_and_exit; - } - - if (gpe_event_info->flags & ACPI_GPE_CAN_WAKE) { - goto unlock_and_exit; } - gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; - if (gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD) { - (void)acpi_raw_disable_gpe(gpe_event_info); - } - -unlock_and_exit: acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } @@ -651,7 +642,7 @@ acpi_install_gpe_block(acpi_handle gpe_device, struct acpi_generic_address *gpe_block_address, u32 register_count, u32 interrupt_number) { - acpi_status status; + acpi_status status = AE_OK; union acpi_operand_object *obj_desc; struct acpi_namespace_node *node; struct acpi_gpe_block_info *gpe_block; @@ -715,10 +706,6 @@ acpi_install_gpe_block(acpi_handle gpe_device, obj_desc->device.gpe_block = gpe_block; - /* Enable the runtime GPEs in the new block */ - - status = acpi_ev_initialize_gpe_block(node, gpe_block); - unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return_ACPI_STATUS(status); @@ -924,3 +911,43 @@ acpi_status acpi_enable_all_runtime_gpes(void) return_ACPI_STATUS(status); } + +/****************************************************************************** + * + * FUNCTION: acpi_update_gpes + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Enable all GPEs that have associated _Lxx or _Exx methods and + * are not pointed to by any device _PRW methods indicating that + * these GPEs are generally intended for system or device wakeup + * (such GPEs have to be enabled directly when the devices whose + * _PRW methods point to them are set up for wakeup signaling). + * + ******************************************************************************/ + +acpi_status acpi_update_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_update_gpes); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } else if (acpi_all_gpes_initialized) { + goto unlock; + } + + status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block, NULL); + if (ACPI_SUCCESS(status)) { + acpi_all_gpes_initialized = TRUE; + } + +unlock: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + + return_ACPI_STATUS(status); +} diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c index 541cbc1544d..ce9314f7945 100644 --- a/drivers/acpi/acpica/evxfregn.c +++ b/drivers/acpi/acpica/evxfregn.c @@ -64,6 +64,12 @@ ACPI_MODULE_NAME("evxfregn") * * DESCRIPTION: Install a handler for all op_regions of a given space_id. * + * NOTE: This function should only be called after acpi_enable_subsystem has + * been called. This is because any _REG methods associated with the Space ID + * are executed here, and these methods can only be safely executed after + * the default handlers have been installed and the hardware has been + * initialized (via acpi_enable_subsystem.) + * ******************************************************************************/ acpi_status acpi_install_address_space_handler(acpi_handle device, diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index 047217303a4..38293fd3e08 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -119,8 +119,8 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc, } /* - * Exit now for SMBus or IPMI address space, it has a non-linear address space - * and the request cannot be directly validated + * Exit now for SMBus or IPMI address space, it has a non-linear + * address space and the request cannot be directly validated */ if (rgn_desc->region.space_id == ACPI_ADR_SPACE_SMBUS || rgn_desc->region.space_id == ACPI_ADR_SPACE_IPMI) { @@ -147,8 +147,7 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc, * (Region length is specified in bytes) */ if (rgn_desc->region.length < - (obj_desc->common_field.base_byte_offset + - field_datum_byte_offset + + (obj_desc->common_field.base_byte_offset + field_datum_byte_offset + obj_desc->common_field.access_byte_width)) { if (acpi_gbl_enable_interpreter_slack) { /* @@ -680,6 +679,7 @@ acpi_ex_extract_from_field(union acpi_operand_object *obj_desc, u32 buffer_tail_bits; u32 datum_count; u32 field_datum_count; + u32 access_bit_width; u32 i; ACPI_FUNCTION_TRACE(ex_extract_from_field); @@ -694,16 +694,36 @@ acpi_ex_extract_from_field(union acpi_operand_object *obj_desc, return_ACPI_STATUS(AE_BUFFER_OVERFLOW); } + ACPI_MEMSET(buffer, 0, buffer_length); + access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width); + + /* Handle the simple case here */ + + if ((obj_desc->common_field.start_field_bit_offset == 0) && + (obj_desc->common_field.bit_length == access_bit_width)) { + status = acpi_ex_field_datum_io(obj_desc, 0, buffer, ACPI_READ); + return_ACPI_STATUS(status); + } + +/* TBD: Move to common setup code */ + + /* Field algorithm is limited to sizeof(u64), truncate if needed */ + + if (obj_desc->common_field.access_byte_width > sizeof(u64)) { + obj_desc->common_field.access_byte_width = sizeof(u64); + access_bit_width = sizeof(u64) * 8; + } /* Compute the number of datums (access width data items) */ - datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length, - obj_desc->common_field.access_bit_width); + datum_count = + ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length, + access_bit_width); + field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length + obj_desc->common_field. start_field_bit_offset, - obj_desc->common_field. access_bit_width); /* Priming read from the field */ @@ -738,12 +758,11 @@ acpi_ex_extract_from_field(union acpi_operand_object *obj_desc, * This avoids the differences in behavior between different compilers * concerning shift values larger than the target data width. */ - if ((obj_desc->common_field.access_bit_width - - obj_desc->common_field.start_field_bit_offset) < + if (access_bit_width - + obj_desc->common_field.start_field_bit_offset < ACPI_INTEGER_BIT_SIZE) { merged_datum |= - raw_datum << (obj_desc->common_field. - access_bit_width - + raw_datum << (access_bit_width - obj_desc->common_field. start_field_bit_offset); } @@ -765,8 +784,7 @@ acpi_ex_extract_from_field(union acpi_operand_object *obj_desc, /* Mask off any extra bits in the last datum */ - buffer_tail_bits = obj_desc->common_field.bit_length % - obj_desc->common_field.access_bit_width; + buffer_tail_bits = obj_desc->common_field.bit_length % access_bit_width; if (buffer_tail_bits) { merged_datum &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits); } @@ -798,6 +816,7 @@ acpi_status acpi_ex_insert_into_field(union acpi_operand_object *obj_desc, void *buffer, u32 buffer_length) { + void *new_buffer; acpi_status status; u64 mask; u64 width_mask; @@ -808,9 +827,9 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc, u32 buffer_tail_bits; u32 datum_count; u32 field_datum_count; - u32 i; + u32 access_bit_width; u32 required_length; - void *new_buffer; + u32 i; ACPI_FUNCTION_TRACE(ex_insert_into_field); @@ -844,17 +863,24 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc, buffer_length = required_length; } +/* TBD: Move to common setup code */ + + /* Algo is limited to sizeof(u64), so cut the access_byte_width */ + if (obj_desc->common_field.access_byte_width > sizeof(u64)) { + obj_desc->common_field.access_byte_width = sizeof(u64); + } + + access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width); + /* * Create the bitmasks used for bit insertion. * Note: This if/else is used to bypass compiler differences with the * shift operator */ - if (obj_desc->common_field.access_bit_width == ACPI_INTEGER_BIT_SIZE) { + if (access_bit_width == ACPI_INTEGER_BIT_SIZE) { width_mask = ACPI_UINT64_MAX; } else { - width_mask = - ACPI_MASK_BITS_ABOVE(obj_desc->common_field. - access_bit_width); + width_mask = ACPI_MASK_BITS_ABOVE(access_bit_width); } mask = width_mask & @@ -863,12 +889,11 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc, /* Compute the number of datums (access width data items) */ datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length, - obj_desc->common_field.access_bit_width); + access_bit_width); field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length + obj_desc->common_field. start_field_bit_offset, - obj_desc->common_field. access_bit_width); /* Get initial Datum from the input buffer */ @@ -905,12 +930,11 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc, * This avoids the differences in behavior between different compilers * concerning shift values larger than the target data width. */ - if ((obj_desc->common_field.access_bit_width - + if ((access_bit_width - obj_desc->common_field.start_field_bit_offset) < ACPI_INTEGER_BIT_SIZE) { merged_datum = - raw_datum >> (obj_desc->common_field. - access_bit_width - + raw_datum >> (access_bit_width - obj_desc->common_field. start_field_bit_offset); } else { @@ -929,6 +953,7 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc, ACPI_MEMCPY(&raw_datum, ((char *)buffer) + buffer_offset, ACPI_MIN(obj_desc->common_field.access_byte_width, buffer_length - buffer_offset)); + merged_datum |= raw_datum << obj_desc->common_field.start_field_bit_offset; } @@ -937,7 +962,7 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc, buffer_tail_bits = (obj_desc->common_field.bit_length + obj_desc->common_field.start_field_bit_offset) % - obj_desc->common_field.access_bit_width; + access_bit_width; if (buffer_tail_bits) { mask &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits); } diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c index f73be97043c..6af14e43f83 100644 --- a/drivers/acpi/acpica/exmutex.c +++ b/drivers/acpi/acpica/exmutex.c @@ -336,7 +336,7 @@ acpi_status acpi_ex_release_mutex_object(union acpi_operand_object *obj_desc) /* Clear mutex info */ - obj_desc->mutex.thread_id = NULL; + obj_desc->mutex.thread_id = 0; return_ACPI_STATUS(status); } @@ -393,10 +393,10 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, if ((owner_thread->thread_id != walk_state->thread->thread_id) && (obj_desc != acpi_gbl_global_lock_mutex)) { ACPI_ERROR((AE_INFO, - "Thread %p cannot release Mutex [%4.4s] acquired by thread %p", - ACPI_CAST_PTR(void, walk_state->thread->thread_id), + "Thread %u cannot release Mutex [%4.4s] acquired by thread %u", + (u32)walk_state->thread->thread_id, acpi_ut_get_node_name(obj_desc->mutex.node), - ACPI_CAST_PTR(void, owner_thread->thread_id))); + (u32)owner_thread->thread_id)); return_ACPI_STATUS(AE_AML_NOT_OWNER); } @@ -488,7 +488,7 @@ void acpi_ex_release_all_mutexes(struct acpi_thread_state *thread) /* Mark mutex unowned */ obj_desc->mutex.owner_thread = NULL; - obj_desc->mutex.thread_id = NULL; + obj_desc->mutex.thread_id = 0; /* Update Thread sync_level (Last mutex is the important one) */ diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c index 98a331d2249..7aae29f73d3 100644 --- a/drivers/acpi/acpica/exprep.c +++ b/drivers/acpi/acpica/exprep.c @@ -355,12 +355,10 @@ acpi_ex_prep_common_field_object(union acpi_operand_object *obj_desc, return_ACPI_STATUS(AE_AML_OPERAND_VALUE); } - /* Setup width (access granularity) fields */ + /* Setup width (access granularity) fields (values are: 1, 2, 4, 8) */ obj_desc->common_field.access_byte_width = (u8) - ACPI_DIV_8(access_bit_width); /* 1, 2, 4, 8 */ - - obj_desc->common_field.access_bit_width = (u8) access_bit_width; + ACPI_DIV_8(access_bit_width); /* * base_byte_offset is the address of the start of the field within the @@ -405,8 +403,9 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info) { union acpi_operand_object *obj_desc; union acpi_operand_object *second_desc = NULL; - u32 type; acpi_status status; + u32 access_byte_width; + u32 type; ACPI_FUNCTION_TRACE(ex_prep_field_value); @@ -421,8 +420,8 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info) type = acpi_ns_get_type(info->region_node); if (type != ACPI_TYPE_REGION) { ACPI_ERROR((AE_INFO, - "Needed Region, found type 0x%X (%s)", - type, acpi_ut_get_type_name(type))); + "Needed Region, found type 0x%X (%s)", type, + acpi_ut_get_type_name(type))); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } @@ -438,7 +437,8 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info) /* Initialize areas of the object that are common to all fields */ obj_desc->common_field.node = info->field_node; - status = acpi_ex_prep_common_field_object(obj_desc, info->field_flags, + status = acpi_ex_prep_common_field_object(obj_desc, + info->field_flags, info->attribute, info->field_bit_position, info->field_bit_length); @@ -455,26 +455,25 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info) obj_desc->field.region_obj = acpi_ns_get_attached_object(info->region_node); - /* An additional reference for the container */ + /* Allow full data read from EC address space */ - acpi_ut_add_reference(obj_desc->field.region_obj); + if ((obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_EC) + && (obj_desc->common_field.bit_length > 8)) { + access_byte_width = + ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field. + bit_length); + + /* Maximum byte width supported is 255 */ - /* allow full data read from EC address space */ - if (obj_desc->field.region_obj->region.space_id == - ACPI_ADR_SPACE_EC) { - if (obj_desc->common_field.bit_length > 8) { - unsigned width = - ACPI_ROUND_BITS_UP_TO_BYTES( - obj_desc->common_field.bit_length); - // access_bit_width is u8, don't overflow it - if (width > 8) - width = 8; + if (access_byte_width < 256) { obj_desc->common_field.access_byte_width = - width; - obj_desc->common_field.access_bit_width = - 8 * width; + (u8)access_byte_width; } } + /* An additional reference for the container */ + + acpi_ut_add_reference(obj_desc->field.region_obj); ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "RegionField: BitOff %X, Off %X, Gran %X, Region %p\n", diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c index 8819d2ac5ae..de17e10da0e 100644 --- a/drivers/acpi/acpica/exregion.c +++ b/drivers/acpi/acpica/exregion.c @@ -353,7 +353,6 @@ acpi_ex_pci_config_space_handler(u32 function, acpi_status status = AE_OK; struct acpi_pci_id *pci_id; u16 pci_register; - u32 value32; ACPI_FUNCTION_TRACE(ex_pci_config_space_handler); @@ -381,8 +380,7 @@ acpi_ex_pci_config_space_handler(u32 function, case ACPI_READ: status = acpi_os_read_pci_configuration(pci_id, pci_register, - &value32, bit_width); - *value = value32; + value, bit_width); break; case ACPI_WRITE: diff --git a/drivers/acpi/acpica/hwpci.c b/drivers/acpi/acpica/hwpci.c new file mode 100644 index 00000000000..ad21c7d8bf4 --- /dev/null +++ b/drivers/acpi/acpica/hwpci.c @@ -0,0 +1,412 @@ +/******************************************************************************* + * + * Module Name: hwpci - Obtain PCI bus, device, and function numbers + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2010, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("hwpci") + +/* PCI configuration space values */ +#define PCI_CFG_HEADER_TYPE_REG 0x0E +#define PCI_CFG_PRIMARY_BUS_NUMBER_REG 0x18 +#define PCI_CFG_SECONDARY_BUS_NUMBER_REG 0x19 +/* PCI header values */ +#define PCI_HEADER_TYPE_MASK 0x7F +#define PCI_TYPE_BRIDGE 0x01 +#define PCI_TYPE_CARDBUS_BRIDGE 0x02 +typedef struct acpi_pci_device { + acpi_handle device; + struct acpi_pci_device *next; + +} acpi_pci_device; + +/* Local prototypes */ + +static acpi_status +acpi_hw_build_pci_list(acpi_handle root_pci_device, + acpi_handle pci_region, + struct acpi_pci_device **return_list_head); + +static acpi_status +acpi_hw_process_pci_list(struct acpi_pci_id *pci_id, + struct acpi_pci_device *list_head); + +static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head); + +static acpi_status +acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id, + acpi_handle pci_device, + u16 *bus_number, u8 *is_bridge); + +/******************************************************************************* + * + * FUNCTION: acpi_hw_derive_pci_id + * + * PARAMETERS: pci_id - Initial values for the PCI ID. May be + * modified by this function. + * root_pci_device - A handle to a PCI device object. This + * object must be a PCI Root Bridge having a + * _HID value of either PNP0A03 or PNP0A08 + * pci_region - A handle to a PCI configuration space + * Operation Region being initialized + * + * RETURN: Status + * + * DESCRIPTION: This function derives a full PCI ID for a PCI device, + * consisting of a Segment number, Bus number, Device number, + * and function code. + * + * The PCI hardware dynamically configures PCI bus numbers + * depending on the bus topology discovered during system + * initialization. This function is invoked during configuration + * of a PCI_Config Operation Region in order to (possibly) update + * the Bus/Device/Function numbers in the pci_id with the actual + * values as determined by the hardware and operating system + * configuration. + * + * The pci_id parameter is initially populated during the Operation + * Region initialization. This function is then called, and is + * will make any necessary modifications to the Bus, Device, or + * Function number PCI ID subfields as appropriate for the + * current hardware and OS configuration. + * + * NOTE: Created 08/2010. Replaces the previous OSL acpi_os_derive_pci_id + * interface since this feature is OS-independent. This module + * specifically avoids any use of recursion by building a local + * temporary device list. + * + ******************************************************************************/ + +acpi_status +acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id, + acpi_handle root_pci_device, acpi_handle pci_region) +{ + acpi_status status; + struct acpi_pci_device *list_head = NULL; + + ACPI_FUNCTION_TRACE(hw_derive_pci_id); + + if (!pci_id) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Build a list of PCI devices, from pci_region up to root_pci_device */ + + status = + acpi_hw_build_pci_list(root_pci_device, pci_region, &list_head); + if (ACPI_SUCCESS(status)) { + + /* Walk the list, updating the PCI device/function/bus numbers */ + + status = acpi_hw_process_pci_list(pci_id, list_head); + } + + /* Always delete the list */ + + acpi_hw_delete_pci_list(list_head); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_build_pci_list + * + * PARAMETERS: root_pci_device - A handle to a PCI device object. This + * object is guaranteed to be a PCI Root + * Bridge having a _HID value of either + * PNP0A03 or PNP0A08 + * pci_region - A handle to the PCI configuration space + * Operation Region + * return_list_head - Where the PCI device list is returned + * + * RETURN: Status + * + * DESCRIPTION: Builds a list of devices from the input PCI region up to the + * Root PCI device for this namespace subtree. + * + ******************************************************************************/ + +static acpi_status +acpi_hw_build_pci_list(acpi_handle root_pci_device, + acpi_handle pci_region, + struct acpi_pci_device **return_list_head) +{ + acpi_handle current_device; + acpi_handle parent_device; + acpi_status status; + struct acpi_pci_device *list_element; + struct acpi_pci_device *list_head = NULL; + + /* + * Ascend namespace branch until the root_pci_device is reached, building + * a list of device nodes. Loop will exit when either the PCI device is + * found, or the root of the namespace is reached. + */ + current_device = pci_region; + while (1) { + status = acpi_get_parent(current_device, &parent_device); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */ + + if (parent_device == root_pci_device) { + *return_list_head = list_head; + return (AE_OK); + } + + list_element = ACPI_ALLOCATE(sizeof(struct acpi_pci_device)); + if (!list_element) { + return (AE_NO_MEMORY); + } + + /* Put new element at the head of the list */ + + list_element->next = list_head; + list_element->device = parent_device; + list_head = list_element; + + current_device = parent_device; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_process_pci_list + * + * PARAMETERS: pci_id - Initial values for the PCI ID. May be + * modified by this function. + * list_head - Device list created by + * acpi_hw_build_pci_list + * + * RETURN: Status + * + * DESCRIPTION: Walk downward through the PCI device list, getting the device + * info for each, via the PCI configuration space and updating + * the PCI ID as necessary. Deletes the list during traversal. + * + ******************************************************************************/ + +static acpi_status +acpi_hw_process_pci_list(struct acpi_pci_id *pci_id, + struct acpi_pci_device *list_head) +{ + acpi_status status = AE_OK; + struct acpi_pci_device *info; + u16 bus_number; + u8 is_bridge = TRUE; + + ACPI_FUNCTION_NAME(hw_process_pci_list); + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Input PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n", + pci_id->segment, pci_id->bus, pci_id->device, + pci_id->function)); + + bus_number = pci_id->bus; + + /* + * Descend down the namespace tree, collecting PCI device, function, + * and bus numbers. bus_number is only important for PCI bridges. + * Algorithm: As we descend the tree, use the last valid PCI device, + * function, and bus numbers that are discovered, and assign them + * to the PCI ID for the target device. + */ + info = list_head; + while (info) { + status = acpi_hw_get_pci_device_info(pci_id, info->device, + &bus_number, &is_bridge); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + info = info->next; + } + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X " + "Status %X BusNumber %X IsBridge %X\n", + pci_id->segment, pci_id->bus, pci_id->device, + pci_id->function, status, bus_number, is_bridge)); + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_delete_pci_list + * + * PARAMETERS: list_head - Device list created by + * acpi_hw_build_pci_list + * + * RETURN: None + * + * DESCRIPTION: Free the entire PCI list. + * + ******************************************************************************/ + +static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head) +{ + struct acpi_pci_device *next; + struct acpi_pci_device *previous; + + next = list_head; + while (next) { + previous = next; + next = previous->next; + ACPI_FREE(previous); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_get_pci_device_info + * + * PARAMETERS: pci_id - Initial values for the PCI ID. May be + * modified by this function. + * pci_device - Handle for the PCI device object + * bus_number - Where a PCI bridge bus number is returned + * is_bridge - Return value, indicates if this PCI + * device is a PCI bridge + * + * RETURN: Status + * + * DESCRIPTION: Get the device info for a single PCI device object. Get the + * _ADR (contains PCI device and function numbers), and for PCI + * bridge devices, get the bus number from PCI configuration + * space. + * + ******************************************************************************/ + +static acpi_status +acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id, + acpi_handle pci_device, + u16 *bus_number, u8 *is_bridge) +{ + acpi_status status; + acpi_object_type object_type; + u64 return_value; + u64 pci_value; + + /* We only care about objects of type Device */ + + status = acpi_get_type(pci_device, &object_type); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (object_type != ACPI_TYPE_DEVICE) { + return (AE_OK); + } + + /* We need an _ADR. Ignore device if not present */ + + status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR, + pci_device, &return_value); + if (ACPI_FAILURE(status)) { + return (AE_OK); + } + + /* + * From _ADR, get the PCI Device and Function and + * update the PCI ID. + */ + pci_id->device = ACPI_HIWORD(ACPI_LODWORD(return_value)); + pci_id->function = ACPI_LOWORD(ACPI_LODWORD(return_value)); + + /* + * If the previous device was a bridge, use the previous + * device bus number + */ + if (*is_bridge) { + pci_id->bus = *bus_number; + } + + /* + * Get the bus numbers from PCI Config space: + * + * First, get the PCI header_type + */ + *is_bridge = FALSE; + status = acpi_os_read_pci_configuration(pci_id, + PCI_CFG_HEADER_TYPE_REG, + &pci_value, 8); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* We only care about bridges (1=pci_bridge, 2=card_bus_bridge) */ + + pci_value &= PCI_HEADER_TYPE_MASK; + + if ((pci_value != PCI_TYPE_BRIDGE) && + (pci_value != PCI_TYPE_CARDBUS_BRIDGE)) { + return (AE_OK); + } + + /* Bridge: Get the Primary bus_number */ + + status = acpi_os_read_pci_configuration(pci_id, + PCI_CFG_PRIMARY_BUS_NUMBER_REG, + &pci_value, 8); + if (ACPI_FAILURE(status)) { + return (status); + } + + *is_bridge = TRUE; + pci_id->bus = (u16)pci_value; + + /* Bridge: Get the Secondary bus_number */ + + status = acpi_os_read_pci_configuration(pci_id, + PCI_CFG_SECONDARY_BUS_NUMBER_REG, + &pci_value, 8); + if (ACPI_FAILURE(status)) { + return (status); + } + + *bus_number = (u16)pci_value; + return (AE_OK); +} diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index 4009498fbab..4ef9f43ea92 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -74,10 +74,18 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data, union acpi_operand_object **return_object_ptr); static acpi_status +acpi_ns_repair_CID(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr); + +static acpi_status acpi_ns_repair_FDE(struct acpi_predefined_data *data, union acpi_operand_object **return_object_ptr); static acpi_status +acpi_ns_repair_HID(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr); + +static acpi_status acpi_ns_repair_PSS(struct acpi_predefined_data *data, union acpi_operand_object **return_object_ptr); @@ -108,8 +116,10 @@ acpi_ns_sort_list(union acpi_operand_object **elements, * As necessary: * * _ALR: Sort the list ascending by ambient_illuminance + * _CID: Strings: uppercase all, remove any leading asterisk * _FDE: Convert Buffer of BYTEs to a Buffer of DWORDs * _GTM: Convert Buffer of BYTEs to a Buffer of DWORDs + * _HID: Strings: uppercase all, remove any leading asterisk * _PSS: Sort the list descending by Power * _TSS: Sort the list descending by Power * @@ -122,8 +132,10 @@ acpi_ns_sort_list(union acpi_operand_object **elements, */ static const struct acpi_repair_info acpi_ns_repairable_names[] = { {"_ALR", acpi_ns_repair_ALR}, + {"_CID", acpi_ns_repair_CID}, {"_FDE", acpi_ns_repair_FDE}, {"_GTM", acpi_ns_repair_FDE}, /* _GTM has same repair as _FDE */ + {"_HID", acpi_ns_repair_HID}, {"_PSS", acpi_ns_repair_PSS}, {"_TSS", acpi_ns_repair_TSS}, {{0, 0, 0, 0}, NULL} /* Table terminator */ @@ -321,6 +333,157 @@ acpi_ns_repair_FDE(struct acpi_predefined_data *data, /****************************************************************************** * + * FUNCTION: acpi_ns_repair_CID + * + * PARAMETERS: Data - Pointer to validation data structure + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _CID object. If a string, ensure that all + * letters are uppercase and that there is no leading asterisk. + * If a Package, ensure same for all string elements. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_CID(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr) +{ + acpi_status status; + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object **element_ptr; + union acpi_operand_object *original_element; + u16 original_ref_count; + u32 i; + + /* Check for _CID as a simple string */ + + if (return_object->common.type == ACPI_TYPE_STRING) { + status = acpi_ns_repair_HID(data, return_object_ptr); + return (status); + } + + /* Exit if not a Package */ + + if (return_object->common.type != ACPI_TYPE_PACKAGE) { + return (AE_OK); + } + + /* Examine each element of the _CID package */ + + element_ptr = return_object->package.elements; + for (i = 0; i < return_object->package.count; i++) { + original_element = *element_ptr; + original_ref_count = original_element->common.reference_count; + + status = acpi_ns_repair_HID(data, element_ptr); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Take care with reference counts */ + + if (original_element != *element_ptr) { + + /* Element was replaced */ + + (*element_ptr)->common.reference_count = + original_ref_count; + + acpi_ut_remove_reference(original_element); + } + + element_ptr++; + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_HID + * + * PARAMETERS: Data - Pointer to validation data structure + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _HID object. If a string, ensure that all + * letters are uppercase and that there is no leading asterisk. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_HID(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object *new_string; + char *source; + char *dest; + + ACPI_FUNCTION_NAME(ns_repair_HID); + + /* We only care about string _HID objects (not integers) */ + + if (return_object->common.type != ACPI_TYPE_STRING) { + return (AE_OK); + } + + if (return_object->string.length == 0) { + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Invalid zero-length _HID or _CID string")); + + /* Return AE_OK anyway, let driver handle it */ + + data->flags |= ACPI_OBJECT_REPAIRED; + return (AE_OK); + } + + /* It is simplest to always create a new string object */ + + new_string = acpi_ut_create_string_object(return_object->string.length); + if (!new_string) { + return (AE_NO_MEMORY); + } + + /* + * Remove a leading asterisk if present. For some unknown reason, there + * are many machines in the field that contains IDs like this. + * + * Examples: "*PNP0C03", "*ACPI0003" + */ + source = return_object->string.pointer; + if (*source == '*') { + source++; + new_string->string.length--; + + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Removed invalid leading asterisk\n", + data->pathname)); + } + + /* + * Copy and uppercase the string. From the ACPI specification: + * + * A valid PNP ID must be of the form "AAA####" where A is an uppercase + * letter and # is a hex digit. A valid ACPI ID must be of the form + * "ACPI####" where # is a hex digit. + */ + for (dest = new_string->string.pointer; *source; dest++, source++) { + *dest = (char)ACPI_TOUPPER(*source); + } + + acpi_ut_remove_reference(return_object); + *return_object_ptr = new_string; + return (AE_OK); +} + +/****************************************************************************** + * * FUNCTION: acpi_ns_repair_TSS * * PARAMETERS: Data - Pointer to validation data structure diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c index e1add3491b0..a7d6ad9c111 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -60,104 +60,6 @@ acpi_name acpi_ns_find_parent_name(struct acpi_namespace_node *node_to_search); /******************************************************************************* * - * FUNCTION: acpi_ns_report_error - * - * PARAMETERS: module_name - Caller's module name (for error output) - * line_number - Caller's line number (for error output) - * internal_name - Name or path of the namespace node - * lookup_status - Exception code from NS lookup - * - * RETURN: None - * - * DESCRIPTION: Print warning message with full pathname - * - ******************************************************************************/ - -void -acpi_ns_report_error(const char *module_name, - u32 line_number, - const char *internal_name, acpi_status lookup_status) -{ - acpi_status status; - u32 bad_name; - char *name = NULL; - - acpi_os_printf("ACPI Error (%s-%04d): ", module_name, line_number); - - if (lookup_status == AE_BAD_CHARACTER) { - - /* There is a non-ascii character in the name */ - - ACPI_MOVE_32_TO_32(&bad_name, - ACPI_CAST_PTR(u32, internal_name)); - acpi_os_printf("[0x%4.4X] (NON-ASCII)", bad_name); - } else { - /* Convert path to external format */ - - status = acpi_ns_externalize_name(ACPI_UINT32_MAX, - internal_name, NULL, &name); - - /* Print target name */ - - if (ACPI_SUCCESS(status)) { - acpi_os_printf("[%s]", name); - } else { - acpi_os_printf("[COULD NOT EXTERNALIZE NAME]"); - } - - if (name) { - ACPI_FREE(name); - } - } - - acpi_os_printf(" Namespace lookup failure, %s\n", - acpi_format_exception(lookup_status)); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ns_report_method_error - * - * PARAMETERS: module_name - Caller's module name (for error output) - * line_number - Caller's line number (for error output) - * Message - Error message to use on failure - * prefix_node - Prefix relative to the path - * Path - Path to the node (optional) - * method_status - Execution status - * - * RETURN: None - * - * DESCRIPTION: Print warning message with full pathname - * - ******************************************************************************/ - -void -acpi_ns_report_method_error(const char *module_name, - u32 line_number, - const char *message, - struct acpi_namespace_node *prefix_node, - const char *path, acpi_status method_status) -{ - acpi_status status; - struct acpi_namespace_node *node = prefix_node; - - acpi_os_printf("ACPI Error (%s-%04d): ", module_name, line_number); - - if (path) { - status = - acpi_ns_get_node(prefix_node, path, ACPI_NS_NO_UPSEARCH, - &node); - if (ACPI_FAILURE(status)) { - acpi_os_printf("[Could not get node by pathname]"); - } - } - - acpi_ns_print_node_pathname(node, message); - acpi_os_printf(", %s\n", acpi_format_exception(method_status)); -} - -/******************************************************************************* - * * FUNCTION: acpi_ns_print_node_pathname * * PARAMETERS: Node - Object diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index 1728cb9bf60..d2ff4325c42 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -49,7 +49,7 @@ ACPI_MODULE_NAME("tbfadt") /* Local prototypes */ -static inline void +static ACPI_INLINE void acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, u8 space_id, u8 byte_width, u64 address); @@ -181,7 +181,7 @@ static struct acpi_fadt_pm_info fadt_pm_info_table[] = { * ******************************************************************************/ -static inline void +static ACPI_INLINE void acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, u8 space_id, u8 byte_width, u64 address) { diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index 98351064005..f21c486929a 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -179,9 +179,8 @@ acpi_debug_print(u32 requested_debug_level, if (thread_id != acpi_gbl_prev_thread_id) { if (ACPI_LV_THREADS & acpi_dbg_level) { acpi_os_printf - ("\n**** Context Switch from TID %p to TID %p ****\n\n", - ACPI_CAST_PTR(void, acpi_gbl_prev_thread_id), - ACPI_CAST_PTR(void, thread_id)); + ("\n**** Context Switch from TID %u to TID %u ****\n\n", + (u32)acpi_gbl_prev_thread_id, (u32)thread_id); } acpi_gbl_prev_thread_id = thread_id; @@ -194,7 +193,7 @@ acpi_debug_print(u32 requested_debug_level, acpi_os_printf("%8s-%04ld ", module_name, line_number); if (ACPI_LV_THREADS & acpi_dbg_level) { - acpi_os_printf("[%p] ", ACPI_CAST_PTR(void, thread_id)); + acpi_os_printf("[%u] ", (u32)thread_id); } acpi_os_printf("[%02ld] %-22.22s: ", diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c index 6dfdeb65349..22f59ef604e 100644 --- a/drivers/acpi/acpica/uteval.c +++ b/drivers/acpi/acpica/uteval.c @@ -48,153 +48,6 @@ #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("uteval") -/* - * Strings supported by the _OSI predefined (internal) method. - * - * March 2009: Removed "Linux" as this host no longer wants to respond true - * for this string. Basically, the only safe OS strings are windows-related - * and in many or most cases represent the only test path within the - * BIOS-provided ASL code. - * - * The second element of each entry is used to track the newest version of - * Windows that the BIOS has requested. - */ -static struct acpi_interface_info acpi_interfaces_supported[] = { - /* Operating System Vendor Strings */ - - {"Windows 2000", ACPI_OSI_WIN_2000}, /* Windows 2000 */ - {"Windows 2001", ACPI_OSI_WIN_XP}, /* Windows XP */ - {"Windows 2001 SP1", ACPI_OSI_WIN_XP_SP1}, /* Windows XP SP1 */ - {"Windows 2001.1", ACPI_OSI_WINSRV_2003}, /* Windows Server 2003 */ - {"Windows 2001 SP2", ACPI_OSI_WIN_XP_SP2}, /* Windows XP SP2 */ - {"Windows 2001.1 SP1", ACPI_OSI_WINSRV_2003_SP1}, /* Windows Server 2003 SP1 - Added 03/2006 */ - {"Windows 2006", ACPI_OSI_WIN_VISTA}, /* Windows Vista - Added 03/2006 */ - {"Windows 2006.1", ACPI_OSI_WINSRV_2008}, /* Windows Server 2008 - Added 09/2009 */ - {"Windows 2006 SP1", ACPI_OSI_WIN_VISTA_SP1}, /* Windows Vista SP1 - Added 09/2009 */ - {"Windows 2009", ACPI_OSI_WIN_7}, /* Windows 7 and Server 2008 R2 - Added 09/2009 */ - - /* Feature Group Strings */ - - {"Extended Address Space Descriptor", 0} - - /* - * All "optional" feature group strings (features that are implemented - * by the host) should be implemented in the host version of - * acpi_os_validate_interface and should not be added here. - */ -}; - -/******************************************************************************* - * - * FUNCTION: acpi_ut_osi_implementation - * - * PARAMETERS: walk_state - Current walk state - * - * RETURN: Status - * - * DESCRIPTION: Implementation of the _OSI predefined control method - * - ******************************************************************************/ - -acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state) -{ - acpi_status status; - union acpi_operand_object *string_desc; - union acpi_operand_object *return_desc; - u32 return_value; - u32 i; - - ACPI_FUNCTION_TRACE(ut_osi_implementation); - - /* Validate the string input argument */ - - string_desc = walk_state->arguments[0].object; - if (!string_desc || (string_desc->common.type != ACPI_TYPE_STRING)) { - return_ACPI_STATUS(AE_TYPE); - } - - /* Create a return object */ - - return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); - if (!return_desc) { - return_ACPI_STATUS(AE_NO_MEMORY); - } - - /* Default return value is 0, NOT SUPPORTED */ - - return_value = 0; - - /* Compare input string to static table of supported interfaces */ - - for (i = 0; i < ACPI_ARRAY_LENGTH(acpi_interfaces_supported); i++) { - if (!ACPI_STRCMP(string_desc->string.pointer, - acpi_interfaces_supported[i].name)) { - /* - * The interface is supported. - * Update the osi_data if necessary. We keep track of the latest - * version of Windows that has been requested by the BIOS. - */ - if (acpi_interfaces_supported[i].value > - acpi_gbl_osi_data) { - acpi_gbl_osi_data = - acpi_interfaces_supported[i].value; - } - - return_value = ACPI_UINT32_MAX; - goto exit; - } - } - - /* - * Did not match the string in the static table, call the host OSL to - * check for a match with one of the optional strings (such as - * "Module Device", "3.0 Thermal Model", etc.) - */ - status = acpi_os_validate_interface(string_desc->string.pointer); - if (ACPI_SUCCESS(status)) { - - /* The interface is supported */ - - return_value = ACPI_UINT32_MAX; - } - -exit: - ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, - "ACPI: BIOS _OSI(%s) is %ssupported\n", - string_desc->string.pointer, return_value == 0 ? "not " : "")); - - /* Complete the return value */ - - return_desc->integer.value = return_value; - walk_state->return_desc = return_desc; - return_ACPI_STATUS (AE_OK); -} - -/******************************************************************************* - * - * FUNCTION: acpi_osi_invalidate - * - * PARAMETERS: interface_string - * - * RETURN: Status - * - * DESCRIPTION: invalidate string in pre-defiend _OSI string list - * - ******************************************************************************/ - -acpi_status acpi_osi_invalidate(char *interface) -{ - int i; - - for (i = 0; i < ACPI_ARRAY_LENGTH(acpi_interfaces_supported); i++) { - if (!ACPI_STRCMP(interface, acpi_interfaces_supported[i].name)) { - *acpi_interfaces_supported[i].name = '\0'; - return AE_OK; - } - } - return AE_NOT_FOUND; -} - /******************************************************************************* * * FUNCTION: acpi_ut_evaluate_object diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index 0558747579e..e87bc6760be 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -154,14 +154,16 @@ ACPI_EXPORT_SYMBOL(acpi_format_exception) * 1) _SB_ is defined to be a device to allow \_SB_._INI to be run * during the initialization sequence. * 2) _TZ_ is defined to be a thermal zone in order to allow ASL code to - * perform a Notify() operation on it. + * perform a Notify() operation on it. 09/2010: Changed to type Device. + * This still allows notifies, but does not confuse host code that + * searches for valid thermal_zone objects. */ const struct acpi_predefined_names acpi_gbl_pre_defined_names[] = { {"_GPE", ACPI_TYPE_LOCAL_SCOPE, NULL}, {"_PR_", ACPI_TYPE_LOCAL_SCOPE, NULL}, {"_SB_", ACPI_TYPE_DEVICE, NULL}, {"_SI_", ACPI_TYPE_LOCAL_SCOPE, NULL}, - {"_TZ_", ACPI_TYPE_THERMAL, NULL}, + {"_TZ_", ACPI_TYPE_DEVICE, NULL}, {"_REV", ACPI_TYPE_INTEGER, (char *)ACPI_CA_SUPPORT_LEVEL}, {"_OS_", ACPI_TYPE_STRING, ACPI_OS_NAME}, {"_GL_", ACPI_TYPE_MUTEX, (char *)1}, @@ -766,6 +768,7 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_gpe_fadt_blocks[0] = NULL; acpi_gbl_gpe_fadt_blocks[1] = NULL; acpi_current_gpe_count = 0; + acpi_all_gpes_initialized = FALSE; /* Global handlers */ @@ -774,6 +777,7 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_exception_handler = NULL; acpi_gbl_init_handler = NULL; acpi_gbl_table_handler = NULL; + acpi_gbl_interface_handler = NULL; /* Global Lock support */ @@ -800,6 +804,7 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_debugger_configuration = DEBUGGER_THREADING; acpi_gbl_db_output_flags = ACPI_DB_CONSOLE_OUTPUT; acpi_gbl_osi_data = 0; + acpi_gbl_osi_mutex = NULL; /* Hardware oriented */ diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c index 1397fadd0d4..d2906328535 100644 --- a/drivers/acpi/acpica/utids.c +++ b/drivers/acpi/acpica/utids.c @@ -48,42 +48,6 @@ #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utids") -/* Local prototypes */ -static void acpi_ut_copy_id_string(char *destination, char *source); - -/******************************************************************************* - * - * FUNCTION: acpi_ut_copy_id_string - * - * PARAMETERS: Destination - Where to copy the string - * Source - Source string - * - * RETURN: None - * - * DESCRIPTION: Copies an ID string for the _HID, _CID, and _UID methods. - * Performs removal of a leading asterisk if present -- workaround - * for a known issue on a bunch of machines. - * - ******************************************************************************/ - -static void acpi_ut_copy_id_string(char *destination, char *source) -{ - - /* - * Workaround for ID strings that have a leading asterisk. This construct - * is not allowed by the ACPI specification (ID strings must be - * alphanumeric), but enough existing machines have this embedded in their - * ID strings that the following code is useful. - */ - if (*source == '*') { - source++; - } - - /* Do the actual copy */ - - ACPI_STRCPY(destination, source); -} - /******************************************************************************* * * FUNCTION: acpi_ut_execute_HID @@ -101,7 +65,6 @@ static void acpi_ut_copy_id_string(char *destination, char *source) * NOTE: Internal function, no parameter validation * ******************************************************************************/ - acpi_status acpi_ut_execute_HID(struct acpi_namespace_node *device_node, struct acpica_device_id **return_id) @@ -147,7 +110,7 @@ acpi_ut_execute_HID(struct acpi_namespace_node *device_node, if (obj_desc->common.type == ACPI_TYPE_INTEGER) { acpi_ex_eisa_id_to_string(hid->string, obj_desc->integer.value); } else { - acpi_ut_copy_id_string(hid->string, obj_desc->string.pointer); + ACPI_STRCPY(hid->string, obj_desc->string.pointer); } hid->length = length; @@ -224,7 +187,7 @@ acpi_ut_execute_UID(struct acpi_namespace_node *device_node, if (obj_desc->common.type == ACPI_TYPE_INTEGER) { acpi_ex_integer_to_string(uid->string, obj_desc->integer.value); } else { - acpi_ut_copy_id_string(uid->string, obj_desc->string.pointer); + ACPI_STRCPY(uid->string, obj_desc->string.pointer); } uid->length = length; @@ -357,8 +320,8 @@ acpi_ut_execute_CID(struct acpi_namespace_node *device_node, /* Copy the String CID from the returned object */ - acpi_ut_copy_id_string(next_id_string, - cid_objects[i]->string.pointer); + ACPI_STRCPY(next_id_string, + cid_objects[i]->string.pointer); length = cid_objects[i]->string.length + 1; } diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index a39c93dac71..c1b1c803ea9 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -117,6 +117,10 @@ void acpi_ut_subsystem_shutdown(void) /* Close the acpi_event Handling */ acpi_ev_terminate(); + + /* Delete any dynamic _OSI interfaces */ + + acpi_ut_interface_terminate(); #endif /* Close the Namespace */ diff --git a/drivers/acpi/acpica/utmath.c b/drivers/acpi/acpica/utmath.c index 35059a14eb7..49cf7b7fd81 100644 --- a/drivers/acpi/acpica/utmath.c +++ b/drivers/acpi/acpica/utmath.c @@ -48,11 +48,27 @@ ACPI_MODULE_NAME("utmath") /* - * Support for double-precision integer divide. This code is included here - * in order to support kernel environments where the double-precision math - * library is not available. + * Optional support for 64-bit double-precision integer divide. This code + * is configurable and is implemented in order to support 32-bit kernel + * environments where a 64-bit double-precision math library is not available. + * + * Support for a more normal 64-bit divide/modulo (with check for a divide- + * by-zero) appears after this optional section of code. */ #ifndef ACPI_USE_NATIVE_DIVIDE +/* Structures used only for 64-bit divide */ +typedef struct uint64_struct { + u32 lo; + u32 hi; + +} uint64_struct; + +typedef union uint64_overlay { + u64 full; + struct uint64_struct part; + +} uint64_overlay; + /******************************************************************************* * * FUNCTION: acpi_ut_short_divide @@ -69,6 +85,7 @@ ACPI_MODULE_NAME("utmath") * 32-bit remainder. * ******************************************************************************/ + acpi_status acpi_ut_short_divide(u64 dividend, u32 divisor, u64 *out_quotient, u32 *out_remainder) diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c index e8d0724ee40..c7d0e05ef5a 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -50,11 +50,6 @@ #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utmisc") -/* - * Common suffix for messages - */ -#define ACPI_COMMON_MSG_SUFFIX \ - acpi_os_printf(" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, module_name, line_number) /******************************************************************************* * * FUNCTION: acpi_ut_validate_exception @@ -1044,160 +1039,3 @@ acpi_ut_walk_package_tree(union acpi_operand_object * source_object, return_ACPI_STATUS(AE_AML_INTERNAL); } - -/******************************************************************************* - * - * FUNCTION: acpi_error, acpi_exception, acpi_warning, acpi_info - * - * PARAMETERS: module_name - Caller's module name (for error output) - * line_number - Caller's line number (for error output) - * Format - Printf format string + additional args - * - * RETURN: None - * - * DESCRIPTION: Print message with module/line/version info - * - ******************************************************************************/ - -void ACPI_INTERNAL_VAR_XFACE -acpi_error(const char *module_name, u32 line_number, const char *format, ...) -{ - va_list args; - - acpi_os_printf("ACPI Error: "); - - va_start(args, format); - acpi_os_vprintf(format, args); - ACPI_COMMON_MSG_SUFFIX; - va_end(args); -} - -void ACPI_INTERNAL_VAR_XFACE -acpi_exception(const char *module_name, - u32 line_number, acpi_status status, const char *format, ...) -{ - va_list args; - - acpi_os_printf("ACPI Exception: %s, ", acpi_format_exception(status)); - - va_start(args, format); - acpi_os_vprintf(format, args); - ACPI_COMMON_MSG_SUFFIX; - va_end(args); -} - -void ACPI_INTERNAL_VAR_XFACE -acpi_warning(const char *module_name, u32 line_number, const char *format, ...) -{ - va_list args; - - acpi_os_printf("ACPI Warning: "); - - va_start(args, format); - acpi_os_vprintf(format, args); - ACPI_COMMON_MSG_SUFFIX; - va_end(args); -} - -void ACPI_INTERNAL_VAR_XFACE -acpi_info(const char *module_name, u32 line_number, const char *format, ...) -{ - va_list args; - - acpi_os_printf("ACPI: "); - - va_start(args, format); - acpi_os_vprintf(format, args); - acpi_os_printf("\n"); - va_end(args); -} - -ACPI_EXPORT_SYMBOL(acpi_error) -ACPI_EXPORT_SYMBOL(acpi_exception) -ACPI_EXPORT_SYMBOL(acpi_warning) -ACPI_EXPORT_SYMBOL(acpi_info) - -/******************************************************************************* - * - * FUNCTION: acpi_ut_predefined_warning - * - * PARAMETERS: module_name - Caller's module name (for error output) - * line_number - Caller's line number (for error output) - * Pathname - Full pathname to the node - * node_flags - From Namespace node for the method/object - * Format - Printf format string + additional args - * - * RETURN: None - * - * DESCRIPTION: Warnings for the predefined validation module. Messages are - * only emitted the first time a problem with a particular - * method/object is detected. This prevents a flood of error - * messages for methods that are repeatedly evaluated. - * -******************************************************************************/ - -void ACPI_INTERNAL_VAR_XFACE -acpi_ut_predefined_warning(const char *module_name, - u32 line_number, - char *pathname, - u8 node_flags, const char *format, ...) -{ - va_list args; - - /* - * Warning messages for this method/object will be disabled after the - * first time a validation fails or an object is successfully repaired. - */ - if (node_flags & ANOBJ_EVALUATED) { - return; - } - - acpi_os_printf("ACPI Warning for %s: ", pathname); - - va_start(args, format); - acpi_os_vprintf(format, args); - ACPI_COMMON_MSG_SUFFIX; - va_end(args); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_predefined_info - * - * PARAMETERS: module_name - Caller's module name (for error output) - * line_number - Caller's line number (for error output) - * Pathname - Full pathname to the node - * node_flags - From Namespace node for the method/object - * Format - Printf format string + additional args - * - * RETURN: None - * - * DESCRIPTION: Info messages for the predefined validation module. Messages - * are only emitted the first time a problem with a particular - * method/object is detected. This prevents a flood of - * messages for methods that are repeatedly evaluated. - * - ******************************************************************************/ - -void ACPI_INTERNAL_VAR_XFACE -acpi_ut_predefined_info(const char *module_name, - u32 line_number, - char *pathname, u8 node_flags, const char *format, ...) -{ - va_list args; - - /* - * Warning messages for this method/object will be disabled after the - * first time a validation fails or an object is successfully repaired. - */ - if (node_flags & ANOBJ_EVALUATED) { - return; - } - - acpi_os_printf("ACPI Info for %s: ", pathname); - - va_start(args, format); - acpi_os_vprintf(format, args); - ACPI_COMMON_MSG_SUFFIX; - va_end(args); -} diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index f5cca3a1300..d9efa495b43 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -86,6 +86,12 @@ acpi_status acpi_ut_mutex_initialize(void) spin_lock_init(acpi_gbl_gpe_lock); spin_lock_init(acpi_gbl_hardware_lock); + /* Mutex for _OSI support */ + status = acpi_os_create_mutex(&acpi_gbl_osi_mutex); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + /* Create the reader/writer lock for namespace access */ status = acpi_ut_create_rw_lock(&acpi_gbl_namespace_rw_lock); @@ -117,6 +123,8 @@ void acpi_ut_mutex_terminate(void) acpi_ut_delete_mutex(i); } + acpi_os_delete_mutex(acpi_gbl_osi_mutex); + /* Delete the spinlocks */ acpi_os_delete_lock(acpi_gbl_gpe_lock); @@ -220,18 +228,17 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) if (acpi_gbl_mutex_info[i].thread_id == this_thread_id) { if (i == mutex_id) { ACPI_ERROR((AE_INFO, - "Mutex [%s] already acquired by this thread [%p]", + "Mutex [%s] already acquired by this thread [%u]", acpi_ut_get_mutex_name (mutex_id), - ACPI_CAST_PTR(void, - this_thread_id))); + (u32)this_thread_id)); return (AE_ALREADY_ACQUIRED); } ACPI_ERROR((AE_INFO, - "Invalid acquire order: Thread %p owns [%s], wants [%s]", - ACPI_CAST_PTR(void, this_thread_id), + "Invalid acquire order: Thread %u owns [%s], wants [%s]", + (u32)this_thread_id, acpi_ut_get_mutex_name(i), acpi_ut_get_mutex_name(mutex_id))); @@ -242,24 +249,24 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) #endif ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %p attempting to acquire Mutex [%s]\n", - ACPI_CAST_PTR(void, this_thread_id), + "Thread %u attempting to acquire Mutex [%s]\n", + (u32)this_thread_id, acpi_ut_get_mutex_name(mutex_id))); status = acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex, ACPI_WAIT_FOREVER); if (ACPI_SUCCESS(status)) { ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %p acquired Mutex [%s]\n", - ACPI_CAST_PTR(void, this_thread_id), + "Thread %u acquired Mutex [%s]\n", + (u32)this_thread_id, acpi_ut_get_mutex_name(mutex_id))); acpi_gbl_mutex_info[mutex_id].use_count++; acpi_gbl_mutex_info[mutex_id].thread_id = this_thread_id; } else { ACPI_EXCEPTION((AE_INFO, status, - "Thread %p could not acquire Mutex [0x%X]", - ACPI_CAST_PTR(void, this_thread_id), mutex_id)); + "Thread %u could not acquire Mutex [0x%X]", + (u32)this_thread_id, mutex_id)); } return (status); @@ -279,10 +286,14 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id) { + acpi_thread_id this_thread_id; + ACPI_FUNCTION_NAME(ut_release_mutex); - ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %p releasing Mutex [%s]\n", - ACPI_CAST_PTR(void, acpi_os_get_thread_id()), + this_thread_id = acpi_os_get_thread_id(); + + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %u releasing Mutex [%s]\n", + (u32)this_thread_id, acpi_ut_get_mutex_name(mutex_id))); if (mutex_id > ACPI_MAX_MUTEX) { diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c new file mode 100644 index 00000000000..18c59a85fdc --- /dev/null +++ b/drivers/acpi/acpica/utosi.c @@ -0,0 +1,380 @@ +/****************************************************************************** + * + * Module Name: utosi - Support for the _OSI predefined control method + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2010, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utosi") + +/* + * Strings supported by the _OSI predefined control method (which is + * implemented internally within this module.) + * + * March 2009: Removed "Linux" as this host no longer wants to respond true + * for this string. Basically, the only safe OS strings are windows-related + * and in many or most cases represent the only test path within the + * BIOS-provided ASL code. + * + * The last element of each entry is used to track the newest version of + * Windows that the BIOS has requested. + */ +static struct acpi_interface_info acpi_default_supported_interfaces[] = { + /* Operating System Vendor Strings */ + + {"Windows 2000", NULL, 0, ACPI_OSI_WIN_2000}, /* Windows 2000 */ + {"Windows 2001", NULL, 0, ACPI_OSI_WIN_XP}, /* Windows XP */ + {"Windows 2001 SP1", NULL, 0, ACPI_OSI_WIN_XP_SP1}, /* Windows XP SP1 */ + {"Windows 2001.1", NULL, 0, ACPI_OSI_WINSRV_2003}, /* Windows Server 2003 */ + {"Windows 2001 SP2", NULL, 0, ACPI_OSI_WIN_XP_SP2}, /* Windows XP SP2 */ + {"Windows 2001.1 SP1", NULL, 0, ACPI_OSI_WINSRV_2003_SP1}, /* Windows Server 2003 SP1 - Added 03/2006 */ + {"Windows 2006", NULL, 0, ACPI_OSI_WIN_VISTA}, /* Windows Vista - Added 03/2006 */ + {"Windows 2006.1", NULL, 0, ACPI_OSI_WINSRV_2008}, /* Windows Server 2008 - Added 09/2009 */ + {"Windows 2006 SP1", NULL, 0, ACPI_OSI_WIN_VISTA_SP1}, /* Windows Vista SP1 - Added 09/2009 */ + {"Windows 2006 SP2", NULL, 0, ACPI_OSI_WIN_VISTA_SP2}, /* Windows Vista SP2 - Added 09/2010 */ + {"Windows 2009", NULL, 0, ACPI_OSI_WIN_7}, /* Windows 7 and Server 2008 R2 - Added 09/2009 */ + + /* Feature Group Strings */ + + {"Extended Address Space Descriptor", NULL, 0, 0} + + /* + * All "optional" feature group strings (features that are implemented + * by the host) should be dynamically added by the host via + * acpi_install_interface and should not be manually added here. + * + * Examples of optional feature group strings: + * + * "Module Device" + * "Processor Device" + * "3.0 Thermal Model" + * "3.0 _SCP Extensions" + * "Processor Aggregator Device" + */ +}; + +/******************************************************************************* + * + * FUNCTION: acpi_ut_initialize_interfaces + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize the global _OSI supported interfaces list + * + ******************************************************************************/ + +acpi_status acpi_ut_initialize_interfaces(void) +{ + u32 i; + + (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + acpi_gbl_supported_interfaces = acpi_default_supported_interfaces; + + /* Link the static list of supported interfaces */ + + for (i = 0; + i < (ACPI_ARRAY_LENGTH(acpi_default_supported_interfaces) - 1); + i++) { + acpi_default_supported_interfaces[i].next = + &acpi_default_supported_interfaces[(acpi_size) i + 1]; + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_interface_terminate + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Delete all interfaces in the global list. Sets + * acpi_gbl_supported_interfaces to NULL. + * + ******************************************************************************/ + +void acpi_ut_interface_terminate(void) +{ + struct acpi_interface_info *next_interface; + + (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + next_interface = acpi_gbl_supported_interfaces; + + while (next_interface) { + acpi_gbl_supported_interfaces = next_interface->next; + + /* Only interfaces added at runtime can be freed */ + + if (next_interface->flags & ACPI_OSI_DYNAMIC) { + ACPI_FREE(next_interface->name); + ACPI_FREE(next_interface); + } + + next_interface = acpi_gbl_supported_interfaces; + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_install_interface + * + * PARAMETERS: interface_name - The interface to install + * + * RETURN: Status + * + * DESCRIPTION: Install the interface into the global interface list. + * Caller MUST hold acpi_gbl_osi_mutex + * + ******************************************************************************/ + +acpi_status acpi_ut_install_interface(acpi_string interface_name) +{ + struct acpi_interface_info *interface_info; + + /* Allocate info block and space for the name string */ + + interface_info = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_interface_info)); + if (!interface_info) { + return (AE_NO_MEMORY); + } + + interface_info->name = + ACPI_ALLOCATE_ZEROED(ACPI_STRLEN(interface_name) + 1); + if (!interface_info->name) { + ACPI_FREE(interface_info); + return (AE_NO_MEMORY); + } + + /* Initialize new info and insert at the head of the global list */ + + ACPI_STRCPY(interface_info->name, interface_name); + interface_info->flags = ACPI_OSI_DYNAMIC; + interface_info->next = acpi_gbl_supported_interfaces; + + acpi_gbl_supported_interfaces = interface_info; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_remove_interface + * + * PARAMETERS: interface_name - The interface to remove + * + * RETURN: Status + * + * DESCRIPTION: Remove the interface from the global interface list. + * Caller MUST hold acpi_gbl_osi_mutex + * + ******************************************************************************/ + +acpi_status acpi_ut_remove_interface(acpi_string interface_name) +{ + struct acpi_interface_info *previous_interface; + struct acpi_interface_info *next_interface; + + previous_interface = next_interface = acpi_gbl_supported_interfaces; + while (next_interface) { + if (!ACPI_STRCMP(interface_name, next_interface->name)) { + + /* Found: name is in either the static list or was added at runtime */ + + if (next_interface->flags & ACPI_OSI_DYNAMIC) { + + /* Interface was added dynamically, remove and free it */ + + if (previous_interface == next_interface) { + acpi_gbl_supported_interfaces = + next_interface->next; + } else { + previous_interface->next = + next_interface->next; + } + + ACPI_FREE(next_interface->name); + ACPI_FREE(next_interface); + } else { + /* + * Interface is in static list. If marked invalid, then it + * does not actually exist. Else, mark it invalid. + */ + if (next_interface->flags & ACPI_OSI_INVALID) { + return (AE_NOT_EXIST); + } + + next_interface->flags |= ACPI_OSI_INVALID; + } + + return (AE_OK); + } + + previous_interface = next_interface; + next_interface = next_interface->next; + } + + /* Interface was not found */ + + return (AE_NOT_EXIST); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_interface + * + * PARAMETERS: interface_name - The interface to find + * + * RETURN: struct acpi_interface_info if found. NULL if not found. + * + * DESCRIPTION: Search for the specified interface name in the global list. + * Caller MUST hold acpi_gbl_osi_mutex + * + ******************************************************************************/ + +struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name) +{ + struct acpi_interface_info *next_interface; + + next_interface = acpi_gbl_supported_interfaces; + while (next_interface) { + if (!ACPI_STRCMP(interface_name, next_interface->name)) { + return (next_interface); + } + + next_interface = next_interface->next; + } + + return (NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_osi_implementation + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Implementation of the _OSI predefined control method. When + * an invocation of _OSI is encountered in the system AML, + * control is transferred to this function. + * + ******************************************************************************/ + +acpi_status acpi_ut_osi_implementation(struct acpi_walk_state * walk_state) +{ + union acpi_operand_object *string_desc; + union acpi_operand_object *return_desc; + struct acpi_interface_info *interface_info; + acpi_interface_handler interface_handler; + u32 return_value; + + ACPI_FUNCTION_TRACE(ut_osi_implementation); + + /* Validate the string input argument (from the AML caller) */ + + string_desc = walk_state->arguments[0].object; + if (!string_desc || (string_desc->common.type != ACPI_TYPE_STRING)) { + return_ACPI_STATUS(AE_TYPE); + } + + /* Create a return object */ + + return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!return_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Default return value is 0, NOT SUPPORTED */ + + return_value = 0; + (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + + /* Lookup the interface in the global _OSI list */ + + interface_info = acpi_ut_get_interface(string_desc->string.pointer); + if (interface_info && !(interface_info->flags & ACPI_OSI_INVALID)) { + /* + * The interface is supported. + * Update the osi_data if necessary. We keep track of the latest + * version of Windows that has been requested by the BIOS. + */ + if (interface_info->value > acpi_gbl_osi_data) { + acpi_gbl_osi_data = interface_info->value; + } + + return_value = ACPI_UINT32_MAX; + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + + /* + * Invoke an optional _OSI interface handler. The host OS may wish + * to do some interface-specific handling. For example, warn about + * certain interfaces or override the true/false support value. + */ + interface_handler = acpi_gbl_interface_handler; + if (interface_handler) { + return_value = + interface_handler(string_desc->string.pointer, + return_value); + } + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO, + "ACPI: BIOS _OSI(\"%s\") is %ssupported\n", + string_desc->string.pointer, + return_value == 0 ? "not " : "")); + + /* Complete the return object */ + + return_desc->integer.value = return_value; + walk_state->return_desc = return_desc; + return_ACPI_STATUS(AE_OK); +} diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index 7f8cefcb2b3..1f484c9a688 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -110,6 +110,15 @@ acpi_status __init acpi_initialize_subsystem(void) return_ACPI_STATUS(status); } + /* Initialize the global OSI interfaces list with the static names */ + + status = acpi_ut_initialize_interfaces(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "During OSI interfaces initialization")); + return_ACPI_STATUS(status); + } + /* If configured, initialize the AML debugger */ ACPI_DEBUGGER_EXEC(status = acpi_db_initialize()); @@ -290,19 +299,6 @@ acpi_status acpi_initialize_objects(u32 flags) } /* - * Complete the GPE initialization for the GPE blocks defined in the FADT - * (GPE block 0 and 1). - * - * NOTE: Currently, there seems to be no need to run the _REG methods - * before enabling the GPEs. - */ - if (!(flags & ACPI_NO_EVENT_INIT)) { - status = acpi_ev_install_fadt_gpes(); - if (ACPI_FAILURE(status)) - return (status); - } - - /* * Empty the caches (delete the cached objects) on the assumption that * the table load filled them up more than they will be at runtime -- * thus wasting non-paged memory. @@ -506,6 +502,7 @@ 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 @@ -529,4 +526,117 @@ acpi_status acpi_purge_cached_objects(void) } ACPI_EXPORT_SYMBOL(acpi_purge_cached_objects) -#endif + +/***************************************************************************** + * + * FUNCTION: acpi_install_interface + * + * PARAMETERS: interface_name - The interface to install + * + * RETURN: Status + * + * DESCRIPTION: Install an _OSI interface to the global list + * + ****************************************************************************/ +acpi_status acpi_install_interface(acpi_string interface_name) +{ + acpi_status status; + struct acpi_interface_info *interface_info; + + /* Parameter validation */ + + if (!interface_name || (ACPI_STRLEN(interface_name) == 0)) { + return (AE_BAD_PARAMETER); + } + + (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + + /* Check if the interface name is already in the global list */ + + interface_info = acpi_ut_get_interface(interface_name); + if (interface_info) { + /* + * The interface already exists in the list. This is OK if the + * interface has been marked invalid -- just clear the bit. + */ + if (interface_info->flags & ACPI_OSI_INVALID) { + interface_info->flags &= ~ACPI_OSI_INVALID; + status = AE_OK; + } else { + status = AE_ALREADY_EXISTS; + } + } else { + /* New interface name, install into the global list */ + + status = acpi_ut_install_interface(interface_name); + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_interface) + +/***************************************************************************** + * + * FUNCTION: acpi_remove_interface + * + * PARAMETERS: interface_name - The interface to remove + * + * RETURN: Status + * + * DESCRIPTION: Remove an _OSI interface from the global list + * + ****************************************************************************/ +acpi_status acpi_remove_interface(acpi_string interface_name) +{ + acpi_status status; + + /* Parameter validation */ + + if (!interface_name || (ACPI_STRLEN(interface_name) == 0)) { + return (AE_BAD_PARAMETER); + } + + (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + + status = acpi_ut_remove_interface(interface_name); + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_remove_interface) + +/***************************************************************************** + * + * FUNCTION: acpi_install_interface_handler + * + * PARAMETERS: Handler - The _OSI interface handler to install + * NULL means "remove existing handler" + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for the predefined _OSI ACPI method. + * invoked during execution of the internal implementation of + * _OSI. A NULL handler simply removes any existing handler. + * + ****************************************************************************/ +acpi_status acpi_install_interface_handler(acpi_interface_handler handler) +{ + acpi_status status = AE_OK; + + (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + + if (handler && acpi_gbl_interface_handler) { + status = AE_ALREADY_EXISTS; + } else { + acpi_gbl_interface_handler = handler; + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_interface_handler) +#endif /* !ACPI_ASL_COMPILER */ diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c new file mode 100644 index 00000000000..6f12e314fba --- /dev/null +++ b/drivers/acpi/acpica/utxferror.c @@ -0,0 +1,415 @@ +/******************************************************************************* + * + * Module Name: utxferror - Various error/warning output functions + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2010, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utxferror") + +/* + * This module is used for the in-kernel ACPICA as well as the ACPICA + * tools/applications. + * + * For the i_aSL compiler case, the output is redirected to stderr so that + * any of the various ACPI errors and warnings do not appear in the output + * files, for either the compiler or disassembler portions of the tool. + */ +#ifdef ACPI_ASL_COMPILER +#include <stdio.h> +extern FILE *acpi_gbl_output_file; + +#define ACPI_MSG_REDIRECT_BEGIN \ + FILE *output_file = acpi_gbl_output_file; \ + acpi_os_redirect_output (stderr); + +#define ACPI_MSG_REDIRECT_END \ + acpi_os_redirect_output (output_file); + +#else +/* + * non-i_aSL case - no redirection, nothing to do + */ +#define ACPI_MSG_REDIRECT_BEGIN +#define ACPI_MSG_REDIRECT_END +#endif +/* + * Common message prefixes + */ +#define ACPI_MSG_ERROR "ACPI Error: " +#define ACPI_MSG_EXCEPTION "ACPI Exception: " +#define ACPI_MSG_WARNING "ACPI Warning: " +#define ACPI_MSG_INFO "ACPI: " +/* + * Common message suffix + */ +#define ACPI_MSG_SUFFIX \ + acpi_os_printf (" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, module_name, line_number) +/******************************************************************************* + * + * FUNCTION: acpi_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * Format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Print "ACPI Error" message with module/line/version info + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_error(const char *module_name, u32 line_number, const char *format, ...) +{ + va_list arg_list; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_ERROR); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); + + ACPI_MSG_REDIRECT_END; +} + +ACPI_EXPORT_SYMBOL(acpi_error) + +/******************************************************************************* + * + * FUNCTION: acpi_exception + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * Status - Status to be formatted + * Format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Print "ACPI Exception" message with module/line/version info + * and decoded acpi_status. + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_exception(const char *module_name, + u32 line_number, acpi_status status, const char *format, ...) +{ + va_list arg_list; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_EXCEPTION "%s, ", + acpi_format_exception(status)); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); + + ACPI_MSG_REDIRECT_END; +} + +ACPI_EXPORT_SYMBOL(acpi_exception) + +/******************************************************************************* + * + * FUNCTION: acpi_warning + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * Format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Print "ACPI Warning" message with module/line/version info + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_warning(const char *module_name, u32 line_number, const char *format, ...) +{ + va_list arg_list; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_WARNING); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); + + ACPI_MSG_REDIRECT_END; +} + +ACPI_EXPORT_SYMBOL(acpi_warning) + +/******************************************************************************* + * + * FUNCTION: acpi_info + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * Format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Print generic "ACPI:" information message. There is no + * module/line/version info in order to keep the message simple. + * + * TBD: module_name and line_number args are not needed, should be removed. + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_info(const char *module_name, u32 line_number, const char *format, ...) +{ + va_list arg_list; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_INFO); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + acpi_os_printf("\n"); + va_end(arg_list); + + ACPI_MSG_REDIRECT_END; +} + +ACPI_EXPORT_SYMBOL(acpi_info) + +/* + * The remainder of this module contains internal error functions that may + * be configured out. + */ +#if !defined (ACPI_NO_ERROR_MESSAGES) && !defined (ACPI_BIN_APP) +/******************************************************************************* + * + * FUNCTION: acpi_ut_predefined_warning + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * Pathname - Full pathname to the node + * node_flags - From Namespace node for the method/object + * Format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Warnings for the predefined validation module. Messages are + * only emitted the first time a problem with a particular + * method/object is detected. This prevents a flood of error + * messages for methods that are repeatedly evaluated. + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_warning(const char *module_name, + u32 line_number, + char *pathname, + u8 node_flags, const char *format, ...) +{ + va_list arg_list; + + /* + * Warning messages for this method/object will be disabled after the + * first time a validation fails or an object is successfully repaired. + */ + if (node_flags & ANOBJ_EVALUATED) { + return; + } + + acpi_os_printf(ACPI_MSG_WARNING "For %s: ", pathname); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_predefined_info + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * Pathname - Full pathname to the node + * node_flags - From Namespace node for the method/object + * Format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Info messages for the predefined validation module. Messages + * are only emitted the first time a problem with a particular + * method/object is detected. This prevents a flood of + * messages for methods that are repeatedly evaluated. + * + ******************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_info(const char *module_name, + u32 line_number, + char *pathname, u8 node_flags, const char *format, ...) +{ + va_list arg_list; + + /* + * Warning messages for this method/object will be disabled after the + * first time a validation fails or an object is successfully repaired. + */ + if (node_flags & ANOBJ_EVALUATED) { + return; + } + + acpi_os_printf(ACPI_MSG_INFO "For %s: ", pathname); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_namespace_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * internal_name - Name or path of the namespace node + * lookup_status - Exception code from NS lookup + * + * RETURN: None + * + * DESCRIPTION: Print error message with the full pathname for the NS node. + * + ******************************************************************************/ + +void +acpi_ut_namespace_error(const char *module_name, + u32 line_number, + const char *internal_name, acpi_status lookup_status) +{ + acpi_status status; + u32 bad_name; + char *name = NULL; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_ERROR); + + if (lookup_status == AE_BAD_CHARACTER) { + + /* There is a non-ascii character in the name */ + + ACPI_MOVE_32_TO_32(&bad_name, + ACPI_CAST_PTR(u32, internal_name)); + acpi_os_printf("[0x%4.4X] (NON-ASCII)", bad_name); + } else { + /* Convert path to external format */ + + status = acpi_ns_externalize_name(ACPI_UINT32_MAX, + internal_name, NULL, &name); + + /* Print target name */ + + if (ACPI_SUCCESS(status)) { + acpi_os_printf("[%s]", name); + } else { + acpi_os_printf("[COULD NOT EXTERNALIZE NAME]"); + } + + if (name) { + ACPI_FREE(name); + } + } + + acpi_os_printf(" Namespace lookup failure, %s", + acpi_format_exception(lookup_status)); + + ACPI_MSG_SUFFIX; + ACPI_MSG_REDIRECT_END; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_method_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * Message - Error message to use on failure + * prefix_node - Prefix relative to the path + * Path - Path to the node (optional) + * method_status - Execution status + * + * RETURN: None + * + * DESCRIPTION: Print error message with the full pathname for the method. + * + ******************************************************************************/ + +void +acpi_ut_method_error(const char *module_name, + u32 line_number, + const char *message, + struct acpi_namespace_node *prefix_node, + const char *path, acpi_status method_status) +{ + acpi_status status; + struct acpi_namespace_node *node = prefix_node; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_ERROR); + + if (path) { + status = + acpi_ns_get_node(prefix_node, path, ACPI_NS_NO_UPSEARCH, + &node); + if (ACPI_FAILURE(status)) { + acpi_os_printf("[Could not get node by pathname]"); + } + } + + acpi_ns_print_node_pathname(node, message); + acpi_os_printf(", %s", acpi_format_exception(method_status)); + + ACPI_MSG_SUFFIX; + ACPI_MSG_REDIRECT_END; +} + +#endif /* ACPI_NO_ERROR_MESSAGES */ diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 98417201e9c..95649d37307 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -42,10 +42,7 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> - -#ifdef CONFIG_ACPI_SYSFS_POWER #include <linux/power_supply.h> -#endif #define PREFIX "ACPI: " @@ -98,13 +95,12 @@ enum { * due to bad math. */ ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, + ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, }; struct acpi_battery { struct mutex lock; -#ifdef CONFIG_ACPI_SYSFS_POWER struct power_supply bat; -#endif struct acpi_device *device; unsigned long update_time; int rate_now; @@ -141,7 +137,6 @@ inline int acpi_battery_present(struct acpi_battery *battery) return battery->device->status.battery_present; } -#ifdef CONFIG_ACPI_SYSFS_POWER static int acpi_battery_technology(struct acpi_battery *battery) { if (!strcasecmp("NiCd", battery->type)) @@ -186,6 +181,7 @@ static int acpi_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { + int ret = 0; struct acpi_battery *battery = to_acpi_battery(psy); if (acpi_battery_present(battery)) { @@ -214,26 +210,44 @@ static int acpi_battery_get_property(struct power_supply *psy, val->intval = battery->cycle_count; break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - val->intval = battery->design_voltage * 1000; + if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->design_voltage * 1000; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = battery->voltage_now * 1000; + if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->voltage_now * 1000; break; case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_POWER_NOW: - val->intval = battery->rate_now * 1000; + if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->rate_now * 1000; break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: - val->intval = battery->design_capacity * 1000; + if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->design_capacity * 1000; break; case POWER_SUPPLY_PROP_CHARGE_FULL: case POWER_SUPPLY_PROP_ENERGY_FULL: - val->intval = battery->full_charge_capacity * 1000; + if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->full_charge_capacity * 1000; break; case POWER_SUPPLY_PROP_CHARGE_NOW: case POWER_SUPPLY_PROP_ENERGY_NOW: - val->intval = battery->capacity_now * 1000; + if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->capacity_now * 1000; break; case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = battery->model_number; @@ -245,9 +259,9 @@ static int acpi_battery_get_property(struct power_supply *psy, val->strval = battery->serial_number; break; default: - return -EINVAL; + ret = -EINVAL; } - return 0; + return ret; } static enum power_supply_property charge_battery_props[] = { @@ -281,7 +295,6 @@ static enum power_supply_property energy_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, }; -#endif #ifdef CONFIG_ACPI_PROCFS_POWER inline char *acpi_battery_units(struct acpi_battery *battery) @@ -412,6 +425,8 @@ static int acpi_battery_get_info(struct acpi_battery *battery) result = extract_package(battery, buffer.pointer, info_offsets, ARRAY_SIZE(info_offsets)); kfree(buffer.pointer); + if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)) + battery->full_charge_capacity = battery->design_capacity; return result; } @@ -448,6 +463,10 @@ static int acpi_battery_get_state(struct acpi_battery *battery) battery->rate_now != -1) battery->rate_now = abs((s16)battery->rate_now); + if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags) + && battery->capacity_now >= 0 && battery->capacity_now <= 100) + battery->capacity_now = (battery->capacity_now * + battery->full_charge_capacity) / 100; return result; } @@ -492,7 +511,6 @@ static int acpi_battery_init_alarm(struct acpi_battery *battery) return acpi_battery_set_alarm(battery); } -#ifdef CONFIG_ACPI_SYSFS_POWER static ssize_t acpi_battery_alarm_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -552,7 +570,6 @@ static void sysfs_remove_battery(struct acpi_battery *battery) power_supply_unregister(&battery->bat); battery->bat.dev = NULL; } -#endif static void acpi_battery_quirks(struct acpi_battery *battery) { @@ -561,6 +578,33 @@ static void acpi_battery_quirks(struct acpi_battery *battery) } } +/* + * According to the ACPI spec, some kinds of primary batteries can + * report percentage battery remaining capacity directly to OS. + * In this case, it reports the Last Full Charged Capacity == 100 + * and BatteryPresentRate == 0xFFFFFFFF. + * + * Now we found some battery reports percentage remaining capacity + * even if it's rechargeable. + * https://bugzilla.kernel.org/show_bug.cgi?id=15979 + * + * Handle this correctly so that they won't break userspace. + */ +static void acpi_battery_quirks2(struct acpi_battery *battery) +{ + if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)) + return ; + + if (battery->full_charge_capacity == 100 && + battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN && + battery->capacity_now >=0 && battery->capacity_now <= 100) { + set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags); + battery->full_charge_capacity = battery->design_capacity; + battery->capacity_now = (battery->capacity_now * + battery->full_charge_capacity) / 100; + } +} + static int acpi_battery_update(struct acpi_battery *battery) { int result, old_present = acpi_battery_present(battery); @@ -568,9 +612,7 @@ static int acpi_battery_update(struct acpi_battery *battery) if (result) return result; if (!acpi_battery_present(battery)) { -#ifdef CONFIG_ACPI_SYSFS_POWER sysfs_remove_battery(battery); -#endif battery->update_time = 0; return 0; } @@ -582,11 +624,11 @@ static int acpi_battery_update(struct acpi_battery *battery) acpi_battery_quirks(battery); acpi_battery_init_alarm(battery); } -#ifdef CONFIG_ACPI_SYSFS_POWER if (!battery->bat.dev) sysfs_add_battery(battery); -#endif - return acpi_battery_get_state(battery); + result = acpi_battery_get_state(battery); + acpi_battery_quirks2(battery); + return result; } /* -------------------------------------------------------------------------- @@ -867,26 +909,20 @@ static void acpi_battery_remove_fs(struct acpi_device *device) static void acpi_battery_notify(struct acpi_device *device, u32 event) { struct acpi_battery *battery = acpi_driver_data(device); -#ifdef CONFIG_ACPI_SYSFS_POWER struct device *old; -#endif if (!battery) return; -#ifdef CONFIG_ACPI_SYSFS_POWER old = battery->bat.dev; -#endif acpi_battery_update(battery); acpi_bus_generate_proc_event(device, event, acpi_battery_present(battery)); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, acpi_battery_present(battery)); -#ifdef CONFIG_ACPI_SYSFS_POWER /* acpi_battery_update could remove power_supply object */ if (old && battery->bat.dev) power_supply_changed(&battery->bat); -#endif } static int acpi_battery_add(struct acpi_device *device) @@ -934,9 +970,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type) #ifdef CONFIG_ACPI_PROCFS_POWER acpi_battery_remove_fs(device); #endif -#ifdef CONFIG_ACPI_SYSFS_POWER sysfs_remove_battery(battery); -#endif mutex_destroy(&battery->lock); kfree(battery); return 0; diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 310e3b9749c..d68bd61072b 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -935,6 +935,12 @@ static int __init acpi_bus_init(void) goto error1; } + /* + * _PDC control method may load dynamic SSDT tables, + * and we need to install the table handler before that. + */ + acpi_sysfs_init(); + acpi_early_processor_set_pdc(); /* @@ -1026,7 +1032,6 @@ static int __init acpi_init(void) acpi_scan_init(); acpi_ec_init(); acpi_power_init(); - acpi_sysfs_init(); acpi_debugfs_init(); acpi_sleep_proc_init(); acpi_wakeup_device_init(); diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 1575a9b51f1..71ef9cd0735 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -338,7 +338,8 @@ static int acpi_button_add(struct acpi_device *device) { struct acpi_button *button; struct input_dev *input; - char *hid, *name, *class; + const char *hid = acpi_device_hid(device); + char *name, *class; int error; button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL); @@ -353,7 +354,6 @@ static int acpi_button_add(struct acpi_device *device) goto err_free_button; } - hid = acpi_device_hid(device); name = acpi_device_name(device); class = acpi_device_class(device); diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 3fe29e992be..81514a4918c 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -725,6 +725,7 @@ static void dock_notify(acpi_handle handle, u32 event, void *data) complete_dock(ds); dock_event(ds, event, DOCK_EVENT); dock_lock(ds, 1); + acpi_update_gpes(); break; } if (dock_present(ds) || dock_in_progress(ds)) @@ -929,7 +930,7 @@ static struct attribute_group dock_attribute_group = { * allocated and initialize a new dock station device. Find all devices * that are on the dock station, and register for dock event notifications. */ -static int dock_add(acpi_handle handle) +static int __init dock_add(acpi_handle handle) { int ret, id; struct dock_station ds, *dock_station; @@ -1023,7 +1024,7 @@ static int dock_remove(struct dock_station *ds) * * This is called by acpi_walk_namespace to look for dock stations. */ -static acpi_status +static __init acpi_status find_dock(acpi_handle handle, u32 lvl, void *context, void **rv) { if (is_dock(handle)) @@ -1032,7 +1033,7 @@ find_dock(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; } -static acpi_status +static __init acpi_status find_bay(acpi_handle handle, u32 lvl, void *context, void **rv) { /* If bay is a dock, it's already handled */ diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index f31291ba94d..372ff80b7b0 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -83,6 +83,11 @@ enum { EC_FLAGS_BLOCKED, /* Transactions are blocked */ }; +/* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */ +static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; +module_param(ec_delay, uint, 0644); +MODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes"); + /* If we find an EC via the ECDT, we need to keep a ptr to its context */ /* External interfaces use first EC only, so remember */ typedef int (*acpi_ec_query_func) (void *data); @@ -210,7 +215,7 @@ static int ec_poll(struct acpi_ec *ec) int repeat = 2; /* number of command restarts */ while (repeat--) { unsigned long delay = jiffies + - msecs_to_jiffies(ACPI_EC_DELAY); + msecs_to_jiffies(ec_delay); do { /* don't sleep with disabled interrupts */ if (EC_FLAGS_MSI || irqs_disabled()) { @@ -265,7 +270,7 @@ static int ec_check_ibf0(struct acpi_ec *ec) static int ec_wait_ibf0(struct acpi_ec *ec) { - unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); + unsigned long delay = jiffies + msecs_to_jiffies(ec_delay); /* interrupt wait manually if GPE mode is not active */ while (time_before(jiffies, delay)) if (wait_event_timeout(ec->wait, ec_check_ibf0(ec), diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index d94d2953c97..60049080c86 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -27,8 +27,6 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> #include <asm/uaccess.h> #include <linux/thermal.h> #include <acpi/acpi_bus.h> @@ -119,122 +117,6 @@ static struct thermal_cooling_device_ops fan_cooling_ops = { }; /* -------------------------------------------------------------------------- - FS Interface (/proc) - -------------------------------------------------------------------------- */ -#ifdef CONFIG_ACPI_PROCFS - -static struct proc_dir_entry *acpi_fan_dir; - -static int acpi_fan_read_state(struct seq_file *seq, void *offset) -{ - struct acpi_device *device = seq->private; - int state = 0; - - - if (device) { - if (acpi_bus_get_power(device->handle, &state)) - seq_printf(seq, "status: ERROR\n"); - else - seq_printf(seq, "status: %s\n", - !state ? "on" : "off"); - } - return 0; -} - -static int acpi_fan_state_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_fan_read_state, PDE(inode)->data); -} - -static ssize_t -acpi_fan_write_state(struct file *file, const char __user * buffer, - size_t count, loff_t * ppos) -{ - int result = 0; - struct seq_file *m = file->private_data; - struct acpi_device *device = m->private; - char state_string[3] = { '\0' }; - - if (count > sizeof(state_string) - 1) - return -EINVAL; - - if (copy_from_user(state_string, buffer, count)) - return -EFAULT; - - state_string[count] = '\0'; - if ((state_string[0] < '0') || (state_string[0] > '3')) - return -EINVAL; - if (state_string[1] == '\n') - state_string[1] = '\0'; - if (state_string[1] != '\0') - return -EINVAL; - - result = acpi_bus_set_power(device->handle, - simple_strtoul(state_string, NULL, 0)); - if (result) - return result; - - return count; -} - -static const struct file_operations acpi_fan_state_ops = { - .open = acpi_fan_state_open_fs, - .read = seq_read, - .write = acpi_fan_write_state, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int acpi_fan_add_fs(struct acpi_device *device) -{ - struct proc_dir_entry *entry = NULL; - - - if (!device) - return -EINVAL; - - if (!acpi_device_dir(device)) { - acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), - acpi_fan_dir); - if (!acpi_device_dir(device)) - return -ENODEV; - } - - /* 'status' [R/W] */ - entry = proc_create_data(ACPI_FAN_FILE_STATE, - S_IFREG | S_IRUGO | S_IWUSR, - acpi_device_dir(device), - &acpi_fan_state_ops, - device); - if (!entry) - return -ENODEV; - return 0; -} - -static int acpi_fan_remove_fs(struct acpi_device *device) -{ - - if (acpi_device_dir(device)) { - remove_proc_entry(ACPI_FAN_FILE_STATE, acpi_device_dir(device)); - remove_proc_entry(acpi_device_bid(device), acpi_fan_dir); - acpi_device_dir(device) = NULL; - } - - return 0; -} -#else -static int acpi_fan_add_fs(struct acpi_device *device) -{ - return 0; -} - -static int acpi_fan_remove_fs(struct acpi_device *device) -{ - return 0; -} -#endif -/* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -284,10 +166,6 @@ static int acpi_fan_add(struct acpi_device *device) dev_err(&device->dev, "Failed to create sysfs link " "'device'\n"); - result = acpi_fan_add_fs(device); - if (result) - goto end; - printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device), acpi_device_bid(device), !device->power.state ? "on" : "off"); @@ -303,7 +181,6 @@ static int acpi_fan_remove(struct acpi_device *device, int type) if (!device || !cdev) return -EINVAL; - acpi_fan_remove_fs(device); sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); sysfs_remove_link(&cdev->device.kobj, "device"); thermal_cooling_device_unregister(cdev); @@ -347,19 +224,9 @@ static int __init acpi_fan_init(void) { int result = 0; -#ifdef CONFIG_ACPI_PROCFS - acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir); - if (!acpi_fan_dir) - return -ENODEV; -#endif - result = acpi_bus_register_driver(&acpi_fan_driver); - if (result < 0) { -#ifdef CONFIG_ACPI_PROCFS - remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir); -#endif + if (result < 0) return -ENODEV; - } return 0; } @@ -369,10 +236,6 @@ static void __exit acpi_fan_exit(void) acpi_bus_unregister_driver(&acpi_fan_driver); -#ifdef CONFIG_ACPI_PROCFS - remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir); -#endif - return; } diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 65b25a303b8..966feddf6b1 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -95,8 +95,25 @@ struct acpi_res_list { static LIST_HEAD(resource_list_head); static DEFINE_SPINLOCK(acpi_res_lock); +/* + * This list of permanent mappings is for memory that may be accessed from + * interrupt context, where we can't do the ioremap(). + */ +struct acpi_ioremap { + struct list_head list; + void __iomem *virt; + acpi_physical_address phys; + acpi_size size; + struct kref ref; +}; + +static LIST_HEAD(acpi_ioremaps); +static DEFINE_SPINLOCK(acpi_ioremap_lock); + #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ -static char osi_additional_string[OSI_STRING_LENGTH_MAX]; +static char osi_setup_string[OSI_STRING_LENGTH_MAX]; + +static void __init acpi_osi_setup_late(void); /* * The story of _OSI(Linux) @@ -138,6 +155,20 @@ static struct osi_linux { unsigned int known:1; } osi_linux = { 0, 0, 0, 0}; +static u32 acpi_osi_handler(acpi_string interface, u32 supported) +{ + if (!strcmp("Linux", interface)) { + + printk(KERN_NOTICE FW_BUG PREFIX + "BIOS _OSI(Linux) query %s%s\n", + osi_linux.enable ? "honored" : "ignored", + osi_linux.cmdline ? " via cmdline" : + osi_linux.dmi ? " via DMI" : ""); + } + + return supported; +} + static void __init acpi_request_region (struct acpi_generic_address *addr, unsigned int length, char *desc) { @@ -185,36 +216,6 @@ static int __init acpi_reserve_resources(void) } device_initcall(acpi_reserve_resources); -acpi_status __init acpi_os_initialize(void) -{ - return AE_OK; -} - -acpi_status acpi_os_initialize1(void) -{ - kacpid_wq = create_workqueue("kacpid"); - kacpi_notify_wq = create_workqueue("kacpi_notify"); - kacpi_hotplug_wq = create_workqueue("kacpi_hotplug"); - BUG_ON(!kacpid_wq); - BUG_ON(!kacpi_notify_wq); - BUG_ON(!kacpi_hotplug_wq); - return AE_OK; -} - -acpi_status acpi_os_terminate(void) -{ - if (acpi_irq_handler) { - acpi_os_remove_interrupt_handler(acpi_irq_irq, - acpi_irq_handler); - } - - destroy_workqueue(kacpid_wq); - destroy_workqueue(kacpi_notify_wq); - destroy_workqueue(kacpi_hotplug_wq); - - return AE_OK; -} - void acpi_os_printf(const char *fmt, ...) { va_list args; @@ -260,29 +261,135 @@ acpi_physical_address __init acpi_os_get_root_pointer(void) } } +/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ +static struct acpi_ioremap * +acpi_map_lookup(acpi_physical_address phys, acpi_size size) +{ + struct acpi_ioremap *map; + + list_for_each_entry_rcu(map, &acpi_ioremaps, list) + if (map->phys <= phys && + phys + size <= map->phys + map->size) + return map; + + return NULL; +} + +/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ +static void __iomem * +acpi_map_vaddr_lookup(acpi_physical_address phys, unsigned int size) +{ + struct acpi_ioremap *map; + + map = acpi_map_lookup(phys, size); + if (map) + return map->virt + (phys - map->phys); + + return NULL; +} + +/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ +static struct acpi_ioremap * +acpi_map_lookup_virt(void __iomem *virt, acpi_size size) +{ + struct acpi_ioremap *map; + + list_for_each_entry_rcu(map, &acpi_ioremaps, list) + if (map->virt <= virt && + virt + size <= map->virt + map->size) + return map; + + return NULL; +} + void __iomem *__init_refok acpi_os_map_memory(acpi_physical_address phys, acpi_size size) { + struct acpi_ioremap *map, *tmp_map; + unsigned long flags, pg_sz; + void __iomem *virt; + phys_addr_t pg_off; + if (phys > ULONG_MAX) { printk(KERN_ERR PREFIX "Cannot map memory that high\n"); return NULL; } - if (acpi_gbl_permanent_mmap) - /* - * ioremap checks to ensure this is in reserved space - */ - return ioremap((unsigned long)phys, size); - else + + if (!acpi_gbl_permanent_mmap) return __acpi_map_table((unsigned long)phys, size); + + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (!map) + return NULL; + + pg_off = round_down(phys, PAGE_SIZE); + pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off; + virt = ioremap(pg_off, pg_sz); + if (!virt) { + kfree(map); + return NULL; + } + + INIT_LIST_HEAD(&map->list); + map->virt = virt; + map->phys = pg_off; + map->size = pg_sz; + kref_init(&map->ref); + + spin_lock_irqsave(&acpi_ioremap_lock, flags); + /* Check if page has already been mapped. */ + tmp_map = acpi_map_lookup(phys, size); + if (tmp_map) { + kref_get(&tmp_map->ref); + spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + iounmap(map->virt); + kfree(map); + return tmp_map->virt + (phys - tmp_map->phys); + } + list_add_tail_rcu(&map->list, &acpi_ioremaps); + spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + + return map->virt + (phys - map->phys); } EXPORT_SYMBOL_GPL(acpi_os_map_memory); +static void acpi_kref_del_iomap(struct kref *ref) +{ + struct acpi_ioremap *map; + + map = container_of(ref, struct acpi_ioremap, ref); + list_del_rcu(&map->list); +} + void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) { - if (acpi_gbl_permanent_mmap) - iounmap(virt); - else + struct acpi_ioremap *map; + unsigned long flags; + int del; + + if (!acpi_gbl_permanent_mmap) { __acpi_unmap_table(virt, size); + return; + } + + spin_lock_irqsave(&acpi_ioremap_lock, flags); + map = acpi_map_lookup_virt(virt, size); + if (!map) { + spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + printk(KERN_ERR PREFIX "%s: bad address %p\n", __func__, virt); + dump_stack(); + return; + } + + del = kref_put(&map->ref, acpi_kref_del_iomap); + spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + + if (!del) + return; + + synchronize_rcu(); + iounmap(map->virt); + kfree(map); } EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); @@ -292,6 +399,44 @@ void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size) __acpi_unmap_table(virt, size); } +int acpi_os_map_generic_address(struct acpi_generic_address *addr) +{ + void __iomem *virt; + + if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) + return 0; + + if (!addr->address || !addr->bit_width) + return -EINVAL; + + virt = acpi_os_map_memory(addr->address, addr->bit_width / 8); + if (!virt) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_os_map_generic_address); + +void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) +{ + void __iomem *virt; + unsigned long flags; + acpi_size size = addr->bit_width / 8; + + if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) + return; + + if (!addr->address || !addr->bit_width) + return; + + spin_lock_irqsave(&acpi_ioremap_lock, flags); + virt = acpi_map_vaddr_lookup(addr->address, size); + spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + + acpi_os_unmap_memory(virt, size); +} +EXPORT_SYMBOL_GPL(acpi_os_unmap_generic_address); + #ifdef ACPI_FUTURE_USAGE acpi_status acpi_os_get_physical_address(void *virt, acpi_physical_address * phys) @@ -495,8 +640,15 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) { u32 dummy; void __iomem *virt_addr; - - virt_addr = ioremap(phys_addr, width); + int size = width / 8, unmap = 0; + + rcu_read_lock(); + virt_addr = acpi_map_vaddr_lookup(phys_addr, size); + rcu_read_unlock(); + if (!virt_addr) { + virt_addr = ioremap(phys_addr, size); + unmap = 1; + } if (!value) value = &dummy; @@ -514,7 +666,8 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) BUG(); } - iounmap(virt_addr); + if (unmap) + iounmap(virt_addr); return AE_OK; } @@ -523,8 +676,15 @@ acpi_status acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) { void __iomem *virt_addr; - - virt_addr = ioremap(phys_addr, width); + int size = width / 8, unmap = 0; + + rcu_read_lock(); + virt_addr = acpi_map_vaddr_lookup(phys_addr, size); + rcu_read_unlock(); + if (!virt_addr) { + virt_addr = ioremap(phys_addr, size); + unmap = 1; + } switch (width) { case 8: @@ -540,16 +700,18 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) BUG(); } - iounmap(virt_addr); + if (unmap) + iounmap(virt_addr); return AE_OK; } acpi_status acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, - u32 *value, u32 width) + u64 *value, u32 width) { int result, size; + u32 value32; if (!value) return AE_BAD_PARAMETER; @@ -570,7 +732,8 @@ acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, result = raw_pci_read(pci_id->segment, pci_id->bus, PCI_DEVFN(pci_id->device, pci_id->function), - reg, size, value); + reg, size, &value32); + *value = value32; return (result ? AE_ERROR : AE_OK); } @@ -602,74 +765,6 @@ acpi_os_write_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, return (result ? AE_ERROR : AE_OK); } -/* TODO: Change code to take advantage of driver model more */ -static void acpi_os_derive_pci_id_2(acpi_handle rhandle, /* upper bound */ - acpi_handle chandle, /* current node */ - struct acpi_pci_id **id, - int *is_bridge, u8 * bus_number) -{ - acpi_handle handle; - struct acpi_pci_id *pci_id = *id; - acpi_status status; - unsigned long long temp; - acpi_object_type type; - - acpi_get_parent(chandle, &handle); - if (handle != rhandle) { - acpi_os_derive_pci_id_2(rhandle, handle, &pci_id, is_bridge, - bus_number); - - status = acpi_get_type(handle, &type); - if ((ACPI_FAILURE(status)) || (type != ACPI_TYPE_DEVICE)) - return; - - status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, - &temp); - if (ACPI_SUCCESS(status)) { - u32 val; - pci_id->device = ACPI_HIWORD(ACPI_LODWORD(temp)); - pci_id->function = ACPI_LOWORD(ACPI_LODWORD(temp)); - - if (*is_bridge) - pci_id->bus = *bus_number; - - /* any nicer way to get bus number of bridge ? */ - status = - acpi_os_read_pci_configuration(pci_id, 0x0e, &val, - 8); - if (ACPI_SUCCESS(status) - && ((val & 0x7f) == 1 || (val & 0x7f) == 2)) { - status = - acpi_os_read_pci_configuration(pci_id, 0x18, - &val, 8); - if (!ACPI_SUCCESS(status)) { - /* Certainly broken... FIX ME */ - return; - } - *is_bridge = 1; - pci_id->bus = val; - status = - acpi_os_read_pci_configuration(pci_id, 0x19, - &val, 8); - if (ACPI_SUCCESS(status)) { - *bus_number = val; - } - } else - *is_bridge = 0; - } - } -} - -void acpi_os_derive_pci_id(acpi_handle rhandle, /* upper bound */ - acpi_handle chandle, /* current node */ - struct acpi_pci_id **id) -{ - int is_bridge = 1; - u8 bus_number = (*id)->bus; - - acpi_os_derive_pci_id_2(rhandle, chandle, id, &is_bridge, &bus_number); -} - static void acpi_os_execute_deferred(struct work_struct *work) { struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); @@ -780,16 +875,6 @@ void acpi_os_wait_events_complete(void *context) EXPORT_SYMBOL(acpi_os_wait_events_complete); /* - * Allocate the memory for a spinlock and initialize it. - */ -acpi_status acpi_os_create_lock(acpi_spinlock * handle) -{ - spin_lock_init(*handle); - - return AE_OK; -} - -/* * Deallocate the memory for a spinlock. */ void acpi_os_delete_lock(acpi_spinlock handle) @@ -977,6 +1062,12 @@ static void __init set_osi_linux(unsigned int enable) printk(KERN_NOTICE PREFIX "%sed _OSI(Linux)\n", enable ? "Add": "Delet"); } + + if (osi_linux.enable) + acpi_osi_setup("Linux"); + else + acpi_osi_setup("!Linux"); + return; } @@ -1011,21 +1102,33 @@ void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d) * string starting with '!' disables that string * otherwise string is added to list, augmenting built-in strings */ -int __init acpi_osi_setup(char *str) +static void __init acpi_osi_setup_late(void) { - if (str == NULL || *str == '\0') { - printk(KERN_INFO PREFIX "_OSI method disabled\n"); - acpi_gbl_create_osi_method = FALSE; - } else if (!strcmp("!Linux", str)) { + char *str = osi_setup_string; + + if (*str == '\0') + return; + + if (!strcmp("!Linux", str)) { acpi_cmdline_osi_linux(0); /* !enable */ } else if (*str == '!') { - if (acpi_osi_invalidate(++str) == AE_OK) + if (acpi_remove_interface(++str) == AE_OK) printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str); } else if (!strcmp("Linux", str)) { acpi_cmdline_osi_linux(1); /* enable */ - } else if (*osi_additional_string == '\0') { - strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX); - printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str); + } else { + if (acpi_install_interface(str) == AE_OK) + printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str); + } +} + +int __init acpi_osi_setup(char *str) +{ + if (str == NULL || *str == '\0') { + printk(KERN_INFO PREFIX "_OSI method disabled\n"); + acpi_gbl_create_osi_method = FALSE; + } else { + strncpy(osi_setup_string, str, OSI_STRING_LENGTH_MAX); } return 1; @@ -1152,21 +1255,6 @@ int acpi_check_region(resource_size_t start, resource_size_t n, } EXPORT_SYMBOL(acpi_check_region); -int acpi_check_mem_region(resource_size_t start, resource_size_t n, - const char *name) -{ - struct resource res = { - .start = start, - .end = start + n - 1, - .name = name, - .flags = IORESOURCE_MEM, - }; - - return acpi_check_resource_conflict(&res); - -} -EXPORT_SYMBOL(acpi_check_mem_region); - /* * Let drivers know whether the resource checks are effective */ @@ -1282,38 +1370,6 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) return (AE_OK); } -/****************************************************************************** - * - * FUNCTION: acpi_os_validate_interface - * - * PARAMETERS: interface - Requested interface to be validated - * - * RETURN: AE_OK if interface is supported, AE_SUPPORT otherwise - * - * DESCRIPTION: Match an interface string to the interfaces supported by the - * host. Strings originate from an AML call to the _OSI method. - * - *****************************************************************************/ - -acpi_status -acpi_os_validate_interface (char *interface) -{ - if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX)) - return AE_OK; - if (!strcmp("Linux", interface)) { - - printk(KERN_NOTICE PREFIX - "BIOS _OSI(Linux) query %s%s\n", - osi_linux.enable ? "honored" : "ignored", - osi_linux.cmdline ? " via cmdline" : - osi_linux.dmi ? " via DMI" : ""); - - if (osi_linux.enable) - return AE_OK; - } - return AE_SUPPORT; -} - static inline int acpi_res_list_add(struct acpi_res_list *res) { struct acpi_res_list *res_list_elem; @@ -1462,5 +1518,46 @@ acpi_os_validate_address ( } return AE_OK; } - #endif + +acpi_status __init acpi_os_initialize(void) +{ + acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1a_event_block); + acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1b_event_block); + acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe0_block); + acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe1_block); + + return AE_OK; +} + +acpi_status acpi_os_initialize1(void) +{ + kacpid_wq = create_workqueue("kacpid"); + kacpi_notify_wq = create_workqueue("kacpi_notify"); + kacpi_hotplug_wq = create_workqueue("kacpi_hotplug"); + BUG_ON(!kacpid_wq); + BUG_ON(!kacpi_notify_wq); + BUG_ON(!kacpi_hotplug_wq); + acpi_install_interface_handler(acpi_osi_handler); + acpi_osi_setup_late(); + return AE_OK; +} + +acpi_status acpi_os_terminate(void) +{ + if (acpi_irq_handler) { + acpi_os_remove_interrupt_handler(acpi_irq_irq, + acpi_irq_handler); + } + + acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe1_block); + acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe0_block); + acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1b_event_block); + acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1a_event_block); + + destroy_workqueue(kacpid_wq); + destroy_workqueue(kacpi_notify_wq); + destroy_workqueue(kacpi_hotplug_wq); + + return AE_OK; +} diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index e4804fb05e2..f907cfbfa13 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -32,7 +32,6 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> -#include <linux/proc_fs.h> #include <linux/spinlock.h> #include <linux/pm.h> #include <linux/pci.h> diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 8d47a5846ae..9ff80a7e9f6 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -34,7 +34,6 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> -#include <linux/proc_fs.h> #include <linux/spinlock.h> #include <linux/pm.h> #include <linux/pci.h> diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 3ba8d1f44a7..96668ad0962 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -27,7 +27,6 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> -#include <linux/proc_fs.h> #include <linux/spinlock.h> #include <linux/pm.h> #include <linux/pm_runtime.h> diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 844c155aeb0..67dedeed144 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -80,18 +80,13 @@ static struct acpi_driver acpi_power_driver = { }, }; -struct acpi_power_reference { - struct list_head node; - struct acpi_device *device; -}; - struct acpi_power_resource { struct acpi_device * device; acpi_bus_id name; u32 system_level; u32 order; + unsigned int ref_count; struct mutex resource_lock; - struct list_head reference; }; static struct list_head acpi_power_resource_list; @@ -184,101 +179,89 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) return result; } -static int acpi_power_on(acpi_handle handle, struct acpi_device *dev) +static int __acpi_power_on(struct acpi_power_resource *resource) { - int result = 0; - int found = 0; acpi_status status = AE_OK; - struct acpi_power_resource *resource = NULL; - struct list_head *node, *next; - struct acpi_power_reference *ref; + status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + /* Update the power resource's _device_ power state */ + resource->device->power.state = ACPI_STATE_D0; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n", + resource->name)); + + return 0; +} + +static int acpi_power_on(acpi_handle handle) +{ + int result = 0; + struct acpi_power_resource *resource = NULL; result = acpi_power_get_context(handle, &resource); if (result) return result; mutex_lock(&resource->resource_lock); - list_for_each_safe(node, next, &resource->reference) { - ref = container_of(node, struct acpi_power_reference, node); - if (dev->handle == ref->device->handle) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n", - dev->pnp.bus_id, resource->name)); - found = 1; - break; - } - } - if (!found) { - ref = kmalloc(sizeof (struct acpi_power_reference), - irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL); - if (!ref) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n")); - mutex_unlock(&resource->resource_lock); - return -ENOMEM; - } - list_add_tail(&ref->node, &resource->reference); - ref->device = dev; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n", - dev->pnp.bus_id, resource->name)); + if (resource->ref_count++) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Power resource [%s] already on", + resource->name)); + } else { + result = __acpi_power_on(resource); } - mutex_unlock(&resource->resource_lock); - status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); - if (ACPI_FAILURE(status)) - return -ENODEV; - - /* Update the power resource's _device_ power state */ - resource->device->power.state = ACPI_STATE_D0; + mutex_unlock(&resource->resource_lock); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n", - resource->name)); return 0; } -static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev) +static int acpi_power_off_device(acpi_handle handle) { int result = 0; acpi_status status = AE_OK; struct acpi_power_resource *resource = NULL; - struct list_head *node, *next; - struct acpi_power_reference *ref; result = acpi_power_get_context(handle, &resource); if (result) return result; mutex_lock(&resource->resource_lock); - list_for_each_safe(node, next, &resource->reference) { - ref = container_of(node, struct acpi_power_reference, node); - if (dev->handle == ref->device->handle) { - list_del(&ref->node); - kfree(ref); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n", - dev->pnp.bus_id, resource->name)); - break; - } + + if (!resource->ref_count) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Power resource [%s] already off", + resource->name)); + goto unlock; } - if (!list_empty(&resource->reference)) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n", - resource->name)); - mutex_unlock(&resource->resource_lock); - return 0; + if (--resource->ref_count) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Power resource [%s] still in use\n", + resource->name)); + goto unlock; } - mutex_unlock(&resource->resource_lock); status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL); - if (ACPI_FAILURE(status)) - return -ENODEV; + if (ACPI_FAILURE(status)) { + result = -ENODEV; + } else { + /* Update the power resource's _device_ power state */ + resource->device->power.state = ACPI_STATE_D3; - /* Update the power resource's _device_ power state */ - resource->device->power.state = ACPI_STATE_D3; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Power resource [%s] turned off\n", + resource->name)); + } - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n", - resource->name)); + unlock: + mutex_unlock(&resource->resource_lock); - return 0; + return result; } /** @@ -364,7 +347,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) /* Open power resource */ for (i = 0; i < dev->wakeup.resources.count; i++) { - int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev); + int ret = acpi_power_on(dev->wakeup.resources.handles[i]); if (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid = 0; @@ -420,7 +403,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) /* Close power resource */ for (i = 0; i < dev->wakeup.resources.count; i++) { int ret = acpi_power_off_device( - dev->wakeup.resources.handles[i], dev); + dev->wakeup.resources.handles[i]); if (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid = 0; @@ -500,7 +483,7 @@ int acpi_power_transition(struct acpi_device *device, int state) * (e.g. so the device doesn't lose power while transitioning). */ for (i = 0; i < tl->count; i++) { - result = acpi_power_on(tl->handles[i], device); + result = acpi_power_on(tl->handles[i]); if (result) goto end; } @@ -513,7 +496,7 @@ int acpi_power_transition(struct acpi_device *device, int state) * Then we dereference all power resources used in the current list. */ for (i = 0; i < cl->count; i++) { - result = acpi_power_off_device(cl->handles[i], device); + result = acpi_power_off_device(cl->handles[i]); if (result) goto end; } @@ -551,7 +534,6 @@ static int acpi_power_add(struct acpi_device *device) resource->device = device; mutex_init(&resource->resource_lock); - INIT_LIST_HEAD(&resource->reference); strcpy(resource->name, device->pnp.bus_id); strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_POWER_CLASS); @@ -594,22 +576,14 @@ static int acpi_power_add(struct acpi_device *device) static int acpi_power_remove(struct acpi_device *device, int type) { - struct acpi_power_resource *resource = NULL; - struct list_head *node, *next; + struct acpi_power_resource *resource; - - if (!device || !acpi_driver_data(device)) + if (!device) return -EINVAL; resource = acpi_driver_data(device); - - mutex_lock(&resource->resource_lock); - list_for_each_safe(node, next, &resource->reference) { - struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node); - list_del(&ref->node); - kfree(ref); - } - mutex_unlock(&resource->resource_lock); + if (!resource) + return -EINVAL; kfree(resource); @@ -619,29 +593,28 @@ static int acpi_power_remove(struct acpi_device *device, int type) static int acpi_power_resume(struct acpi_device *device) { int result = 0, state; - struct acpi_power_resource *resource = NULL; - struct acpi_power_reference *ref; + struct acpi_power_resource *resource; - if (!device || !acpi_driver_data(device)) + if (!device) return -EINVAL; resource = acpi_driver_data(device); + if (!resource) + return -EINVAL; + + mutex_lock(&resource->resource_lock); result = acpi_power_get_state(device->handle, &state); if (result) - return result; + goto unlock; - mutex_lock(&resource->resource_lock); - if (state == ACPI_POWER_RESOURCE_STATE_OFF && - !list_empty(&resource->reference)) { - ref = container_of(resource->reference.next, struct acpi_power_reference, node); - mutex_unlock(&resource->resource_lock); - result = acpi_power_on(device->handle, ref->device); - return result; - } + if (state == ACPI_POWER_RESOURCE_STATE_OFF && resource->ref_count) + result = __acpi_power_on(resource); + unlock: mutex_unlock(&resource->resource_lock); - return 0; + + return result; } int __init acpi_power_init(void) diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 347eb21b235..85e48047d7b 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -40,8 +40,10 @@ #include <linux/pm.h> #include <linux/cpufreq.h> #include <linux/cpu.h> +#ifdef CONFIG_ACPI_PROCFS #include <linux/proc_fs.h> #include <linux/seq_file.h> +#endif #include <linux/dmi.h> #include <linux/moduleparam.h> #include <linux/cpuidle.h> @@ -244,6 +246,7 @@ static int acpi_processor_errata(struct acpi_processor *pr) return result; } +#ifdef CONFIG_ACPI_PROCFS static struct proc_dir_entry *acpi_processor_dir = NULL; static int __cpuinit acpi_processor_add_fs(struct acpi_device *device) @@ -280,7 +283,16 @@ static int acpi_processor_remove_fs(struct acpi_device *device) return 0; } - +#else +static inline int acpi_processor_add_fs(struct acpi_device *device) +{ + return 0; +} +static inline int acpi_processor_remove_fs(struct acpi_device *device) +{ + return 0; +} +#endif /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -842,9 +854,11 @@ static int __init acpi_processor_init(void) memset(&errata, 0, sizeof(errata)); +#ifdef CONFIG_ACPI_PROCFS acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, acpi_root_dir); if (!acpi_processor_dir) return -ENOMEM; +#endif if (!cpuidle_register_driver(&acpi_idle_driver)) { printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n", @@ -871,7 +885,9 @@ static int __init acpi_processor_init(void) out_cpuidle: cpuidle_unregister_driver(&acpi_idle_driver); +#ifdef CONFIG_ACPI_PROCFS remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir); +#endif return result; } @@ -891,7 +907,9 @@ static void __exit acpi_processor_exit(void) cpuidle_unregister_driver(&acpi_idle_driver); +#ifdef CONFIG_ACPI_PROCFS remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir); +#endif return; } @@ -899,6 +917,4 @@ static void __exit acpi_processor_exit(void) module_init(acpi_processor_init); module_exit(acpi_processor_exit); -EXPORT_SYMBOL(acpi_processor_set_thermal_limit); - MODULE_ALIAS("processor"); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index f4428e82b35..dcb38f8ddfd 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -64,7 +64,6 @@ #define ACPI_PROCESSOR_CLASS "processor" #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_idle"); -#define ACPI_PROCESSOR_FILE_POWER "power" #define PM_TIMER_TICK_NS (1000000000ULL/PM_TIMER_FREQUENCY) #define C2_OVERHEAD 1 /* 1us */ #define C3_OVERHEAD 1 /* 1us */ @@ -1013,7 +1012,6 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); state->exit_latency = cx->latency; state->target_residency = cx->latency * latency_factor; - state->power_usage = cx->power; state->flags = 0; switch (cx->type) { diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index 953b25fb986..fde49b9b1d9 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c @@ -44,47 +44,6 @@ #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_thermal"); -/* -------------------------------------------------------------------------- - Limit Interface - -------------------------------------------------------------------------- */ -static int acpi_processor_apply_limit(struct acpi_processor *pr) -{ - int result = 0; - u16 px = 0; - u16 tx = 0; - - - if (!pr) - return -EINVAL; - - if (!pr->flags.limit) - return -ENODEV; - - if (pr->flags.throttling) { - if (pr->limit.user.tx > tx) - tx = pr->limit.user.tx; - if (pr->limit.thermal.tx > tx) - tx = pr->limit.thermal.tx; - - result = acpi_processor_set_throttling(pr, tx, false); - if (result) - goto end; - } - - pr->limit.state.px = px; - pr->limit.state.tx = tx; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Processor [%d] limit set to (P%d:T%d)\n", pr->id, - pr->limit.state.px, pr->limit.state.tx)); - - end: - if (result) - printk(KERN_ERR PREFIX "Unable to set limit\n"); - - return result; -} - #ifdef CONFIG_CPU_FREQ /* If a passive cooling situation is detected, primarily CPUfreq is used, as it @@ -107,36 +66,6 @@ static int cpu_has_cpufreq(unsigned int cpu) return 1; } -static int acpi_thermal_cpufreq_increase(unsigned int cpu) -{ - if (!cpu_has_cpufreq(cpu)) - return -ENODEV; - - if (per_cpu(cpufreq_thermal_reduction_pctg, cpu) < - CPUFREQ_THERMAL_MAX_STEP) { - per_cpu(cpufreq_thermal_reduction_pctg, cpu)++; - cpufreq_update_policy(cpu); - return 0; - } - - return -ERANGE; -} - -static int acpi_thermal_cpufreq_decrease(unsigned int cpu) -{ - if (!cpu_has_cpufreq(cpu)) - return -ENODEV; - - if (per_cpu(cpufreq_thermal_reduction_pctg, cpu) > - (CPUFREQ_THERMAL_MIN_STEP + 1)) - per_cpu(cpufreq_thermal_reduction_pctg, cpu)--; - else - per_cpu(cpufreq_thermal_reduction_pctg, cpu) = 0; - cpufreq_update_policy(cpu); - /* We reached max freq again and can leave passive mode */ - return !per_cpu(cpufreq_thermal_reduction_pctg, cpu); -} - static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb, unsigned long event, void *data) { @@ -238,113 +167,6 @@ static int acpi_thermal_cpufreq_decrease(unsigned int cpu) #endif -int acpi_processor_set_thermal_limit(acpi_handle handle, int type) -{ - int result = 0; - struct acpi_processor *pr = NULL; - struct acpi_device *device = NULL; - int tx = 0, max_tx_px = 0; - - - if ((type < ACPI_PROCESSOR_LIMIT_NONE) - || (type > ACPI_PROCESSOR_LIMIT_DECREMENT)) - return -EINVAL; - - result = acpi_bus_get_device(handle, &device); - if (result) - return result; - - pr = acpi_driver_data(device); - if (!pr) - return -ENODEV; - - /* Thermal limits are always relative to the current Px/Tx state. */ - if (pr->flags.throttling) - pr->limit.thermal.tx = pr->throttling.state; - - /* - * Our default policy is to only use throttling at the lowest - * performance state. - */ - - tx = pr->limit.thermal.tx; - - switch (type) { - - case ACPI_PROCESSOR_LIMIT_NONE: - do { - result = acpi_thermal_cpufreq_decrease(pr->id); - } while (!result); - tx = 0; - break; - - case ACPI_PROCESSOR_LIMIT_INCREMENT: - /* if going up: P-states first, T-states later */ - - result = acpi_thermal_cpufreq_increase(pr->id); - if (!result) - goto end; - else if (result == -ERANGE) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "At maximum performance state\n")); - - if (pr->flags.throttling) { - if (tx == (pr->throttling.state_count - 1)) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "At maximum throttling state\n")); - else - tx++; - } - break; - - case ACPI_PROCESSOR_LIMIT_DECREMENT: - /* if going down: T-states first, P-states later */ - - if (pr->flags.throttling) { - if (tx == 0) { - max_tx_px = 1; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "At minimum throttling state\n")); - } else { - tx--; - goto end; - } - } - - result = acpi_thermal_cpufreq_decrease(pr->id); - if (result) { - /* - * We only could get -ERANGE, 1 or 0. - * In the first two cases we reached max freq again. - */ - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "At minimum performance state\n")); - max_tx_px = 1; - } else - max_tx_px = 0; - - break; - } - - end: - if (pr->flags.throttling) { - pr->limit.thermal.px = 0; - pr->limit.thermal.tx = tx; - - result = acpi_processor_apply_limit(pr); - if (result) - printk(KERN_ERR PREFIX "Unable to set thermal limit\n"); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Thermal limit now (P%d:T%d)\n", - pr->limit.thermal.px, pr->limit.thermal.tx)); - } else - result = 0; - if (max_tx_px) - return 1; - else - return result; -} - int acpi_processor_get_limit_info(struct acpi_processor *pr) { diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 730863855ed..ff3632717c5 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -32,8 +32,10 @@ #include <linux/init.h> #include <linux/sched.h> #include <linux/cpufreq.h> +#ifdef CONFIG_ACPI_PROCFS #include <linux/proc_fs.h> #include <linux/seq_file.h> +#endif #include <asm/io.h> #include <asm/uaccess.h> @@ -1214,6 +1216,7 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) return result; } +#ifdef CONFIG_ACPI_PROCFS /* proc interface */ static int acpi_processor_throttling_seq_show(struct seq_file *seq, void *offset) @@ -1322,3 +1325,4 @@ const struct file_operations acpi_processor_throttling_fops = { .llseek = seq_lseek, .release = single_release, }; +#endif diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 4ff76e8174e..e5dbedb16bb 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -40,10 +40,7 @@ #include <linux/timer.h> #include <linux/jiffies.h> #include <linux/delay.h> - -#ifdef CONFIG_ACPI_SYSFS_POWER #include <linux/power_supply.h> -#endif #include "sbshc.h" @@ -85,9 +82,7 @@ static const struct acpi_device_id sbs_device_ids[] = { MODULE_DEVICE_TABLE(acpi, sbs_device_ids); struct acpi_battery { -#ifdef CONFIG_ACPI_SYSFS_POWER struct power_supply bat; -#endif struct acpi_sbs *sbs; #ifdef CONFIG_ACPI_PROCFS_POWER struct proc_dir_entry *proc_entry; @@ -120,9 +115,7 @@ struct acpi_battery { #define to_acpi_battery(x) container_of(x, struct acpi_battery, bat); struct acpi_sbs { -#ifdef CONFIG_ACPI_SYSFS_POWER struct power_supply charger; -#endif struct acpi_device *device; struct acpi_smb_hc *hc; struct mutex lock; @@ -166,7 +159,6 @@ static inline int acpi_battery_scale(struct acpi_battery *battery) acpi_battery_ipscale(battery); } -#ifdef CONFIG_ACPI_SYSFS_POWER static int sbs_get_ac_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -313,7 +305,6 @@ static enum power_supply_property sbs_energy_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; -#endif /* -------------------------------------------------------------------------- Smart Battery System Management @@ -449,7 +440,6 @@ static int acpi_ac_get_present(struct acpi_sbs *sbs) return result; } -#ifdef CONFIG_ACPI_SYSFS_POWER static ssize_t acpi_battery_alarm_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -479,7 +469,6 @@ static struct device_attribute alarm_attr = { .show = acpi_battery_alarm_show, .store = acpi_battery_alarm_store, }; -#endif /* -------------------------------------------------------------------------- FS Interface (/proc/acpi) @@ -798,7 +787,6 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) &acpi_battery_state_fops, &acpi_battery_alarm_fops, battery); #endif -#ifdef CONFIG_ACPI_SYSFS_POWER battery->bat.name = battery->name; battery->bat.type = POWER_SUPPLY_TYPE_BATTERY; if (!acpi_battery_mode(battery)) { @@ -819,7 +807,6 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) goto end; battery->have_sysfs_alarm = 1; end: -#endif printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n", ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), battery->name, battery->present ? "present" : "absent"); @@ -828,17 +815,13 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) static void acpi_battery_remove(struct acpi_sbs *sbs, int id) { -#if defined(CONFIG_ACPI_SYSFS_POWER) || defined(CONFIG_ACPI_PROCFS_POWER) struct acpi_battery *battery = &sbs->battery[id]; -#endif -#ifdef CONFIG_ACPI_SYSFS_POWER if (battery->bat.dev) { if (battery->have_sysfs_alarm) device_remove_file(battery->bat.dev, &alarm_attr); power_supply_unregister(&battery->bat); } -#endif #ifdef CONFIG_ACPI_PROCFS_POWER if (battery->proc_entry) acpi_sbs_remove_fs(&battery->proc_entry, acpi_battery_dir); @@ -859,14 +842,12 @@ static int acpi_charger_add(struct acpi_sbs *sbs) if (result) goto end; #endif -#ifdef CONFIG_ACPI_SYSFS_POWER sbs->charger.name = "sbs-charger"; sbs->charger.type = POWER_SUPPLY_TYPE_MAINS; sbs->charger.properties = sbs_ac_props; sbs->charger.num_properties = ARRAY_SIZE(sbs_ac_props); sbs->charger.get_property = sbs_get_ac_property; power_supply_register(&sbs->device->dev, &sbs->charger); -#endif printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n", ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), ACPI_AC_DIR_NAME, sbs->charger_present ? "on-line" : "off-line"); @@ -876,10 +857,8 @@ static int acpi_charger_add(struct acpi_sbs *sbs) static void acpi_charger_remove(struct acpi_sbs *sbs) { -#ifdef CONFIG_ACPI_SYSFS_POWER if (sbs->charger.dev) power_supply_unregister(&sbs->charger); -#endif #ifdef CONFIG_ACPI_PROCFS_POWER if (sbs->charger_entry) acpi_sbs_remove_fs(&sbs->charger_entry, acpi_ac_dir); @@ -900,9 +879,7 @@ static void acpi_sbs_callback(void *context) ACPI_SBS_NOTIFY_STATUS, sbs->charger_present); #endif -#ifdef CONFIG_ACPI_SYSFS_POWER kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE); -#endif } if (sbs->manager_present) { for (id = 0; id < MAX_SBS_BAT; ++id) { @@ -919,9 +896,7 @@ static void acpi_sbs_callback(void *context) ACPI_SBS_NOTIFY_STATUS, bat->present); #endif -#ifdef CONFIG_ACPI_SYSFS_POWER kobject_uevent(&bat->bat.dev->kobj, KOBJ_CHANGE); -#endif } } } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b23825ecfa3..2b6c21d86b9 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -26,6 +26,8 @@ extern struct acpi_device *acpi_root; #define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent) +static const char *dummy_hid = "device"; + static LIST_HEAD(acpi_device_list); static LIST_HEAD(acpi_bus_id_list); DEFINE_MUTEX(acpi_device_lock); @@ -49,6 +51,9 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias, int count; struct acpi_hardware_id *id; + if (list_empty(&acpi_dev->pnp.ids)) + return 0; + len = snprintf(modalias, size, "acpi:"); size -= len; @@ -202,13 +207,15 @@ static int acpi_device_setup_files(struct acpi_device *dev) goto end; } - result = device_create_file(&dev->dev, &dev_attr_hid); - if (result) - goto end; + if (!list_empty(&dev->pnp.ids)) { + result = device_create_file(&dev->dev, &dev_attr_hid); + if (result) + goto end; - result = device_create_file(&dev->dev, &dev_attr_modalias); - if (result) - goto end; + result = device_create_file(&dev->dev, &dev_attr_modalias); + if (result) + goto end; + } /* * If device has _EJ0, 'eject' file is created that is used to trigger @@ -316,6 +323,9 @@ static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) struct acpi_device *acpi_dev = to_acpi_device(dev); int len; + if (list_empty(&acpi_dev->pnp.ids)) + return 0; + if (add_uevent_var(env, "MODALIAS=")) return -ENOMEM; len = create_modalias(acpi_dev, &env->buf[env->buflen - 1], @@ -1010,10 +1020,13 @@ static int acpi_dock_match(struct acpi_device *device) return acpi_get_handle(device->handle, "_DCK", &tmp); } -char *acpi_device_hid(struct acpi_device *device) +const char *acpi_device_hid(struct acpi_device *device) { struct acpi_hardware_id *hid; + if (list_empty(&device->pnp.ids)) + return dummy_hid; + hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list); return hid->id; } @@ -1142,16 +1155,6 @@ static void acpi_device_set_id(struct acpi_device *device) acpi_add_id(device, ACPI_BUTTON_HID_SLEEPF); break; } - - /* - * We build acpi_devices for some objects that don't have _HID or _CID, - * e.g., PCI bridges and slots. Drivers can't bind to these objects, - * but we do use them indirectly by traversing the acpi_device tree. - * This generic ID isn't useful for driver binding, but it provides - * the useful property that "every acpi_device has an ID." - */ - if (list_empty(&device->pnp.ids)) - acpi_add_id(device, "device"); } static int acpi_device_set_context(struct acpi_device *device) @@ -1431,6 +1434,7 @@ EXPORT_SYMBOL(acpi_bus_add); int acpi_bus_start(struct acpi_device *device) { struct acpi_bus_ops ops; + int result; if (!device) return -EINVAL; @@ -1438,7 +1442,11 @@ int acpi_bus_start(struct acpi_device *device) memset(&ops, 0, sizeof(ops)); ops.acpi_op_start = 1; - return acpi_bus_scan(device->handle, &ops, NULL); + result = acpi_bus_scan(device->handle, &ops, NULL); + + acpi_update_gpes(); + + return result; } EXPORT_SYMBOL(acpi_bus_start); @@ -1552,6 +1560,8 @@ int __init acpi_scan_init(void) if (result) acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL); + else + acpi_update_gpes(); return result; } diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 4754ff6e70e..721d93b3cee 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -25,7 +25,9 @@ #include "internal.h" #include "sleep.h" -u8 sleep_states[ACPI_S_STATE_COUNT]; +static u8 sleep_states[ACPI_S_STATE_COUNT]; + +static u32 acpi_target_sleep_state = ACPI_STATE_S0; static void acpi_sleep_tts_switch(u32 acpi_state) { @@ -79,8 +81,6 @@ static int acpi_sleep_prepare(u32 acpi_state) } #ifdef CONFIG_ACPI_SLEEP -static u32 acpi_target_sleep_state = ACPI_STATE_S0; - /* * The ACPI specification wants us to save NVS memory regions during hibernation * and to restore them during the subsequent resume. Windows does that also for @@ -419,6 +419,14 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Everex StepNote Series"), }, }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VPCEB1Z1E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1Z1E"), + }, + }, {}, }; #endif /* CONFIG_SUSPEND */ @@ -562,7 +570,7 @@ int acpi_suspend(u32 acpi_state) return -EINVAL; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM_OPS /** * acpi_pm_device_sleep_state - return preferred power state of ACPI device * in the system sleep state given by %acpi_target_sleep_state @@ -624,7 +632,7 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) * can wake the system. _S0W may be valid, too. */ if (acpi_target_sleep_state == ACPI_STATE_S0 || - (device_may_wakeup(dev) && adev->wakeup.state.enabled && + (device_may_wakeup(dev) && adev->wakeup.sleep_state <= acpi_target_sleep_state)) { acpi_status status; @@ -632,7 +640,9 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) status = acpi_evaluate_integer(handle, acpi_method, NULL, &d_max); if (ACPI_FAILURE(status)) { - d_max = d_min; + if (acpi_target_sleep_state != ACPI_STATE_S0 || + status != AE_NOT_FOUND) + d_max = d_min; } else if (d_max < d_min) { /* Warn the user of the broken DSDT */ printk(KERN_WARNING "ACPI: Wrong value from %s\n", @@ -646,7 +656,9 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) *d_min_p = d_min; return d_max; } +#endif /* CONFIG_PM_OPS */ +#ifdef CONFIG_PM_SLEEP /** * acpi_pm_device_sleep_wake - enable or disable the system wake-up * capability of given device @@ -677,7 +689,7 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable) return error; } -#endif +#endif /* CONFIG_PM_SLEEP */ static void acpi_power_off_prepare(void) { @@ -702,7 +714,7 @@ static void acpi_power_off(void) * paths through the BIOS, so disable _GTS and _BFS by default, * but do speak up and offer the option to enable them. */ -void __init acpi_gts_bfs_check(void) +static void __init acpi_gts_bfs_check(void) { acpi_handle dummy; diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h index d8821805c3b..74d59c8f467 100644 --- a/drivers/acpi/sleep.h +++ b/drivers/acpi/sleep.h @@ -1,5 +1,4 @@ -extern u8 sleep_states[]; extern int acpi_suspend(u32 state); extern void acpi_enable_wakeup_devices(u8 sleep_state); diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 2f8f17131d9..5a27b0a3131 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -37,12 +37,6 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/types.h> - -#ifdef CONFIG_ACPI_PROCFS -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#endif - #include <linux/jiffies.h> #include <linux/kmod.h> #include <linux/reboot.h> @@ -195,61 +189,6 @@ struct acpi_thermal { struct mutex lock; }; -#ifdef CONFIG_ACPI_PROCFS -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 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, - loff_t *); -static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file); -static ssize_t acpi_thermal_write_polling(struct file *, const char __user *, - size_t, loff_t *); - -static const struct file_operations acpi_thermal_state_fops = { - .owner = THIS_MODULE, - .open = acpi_thermal_state_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations acpi_thermal_temp_fops = { - .owner = THIS_MODULE, - .open = acpi_thermal_temp_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations acpi_thermal_trip_fops = { - .owner = THIS_MODULE, - .open = acpi_thermal_trip_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations acpi_thermal_cooling_fops = { - .owner = THIS_MODULE, - .open = acpi_thermal_cooling_open_fs, - .read = seq_read, - .write = acpi_thermal_write_cooling_mode, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations acpi_thermal_polling_fops = { - .owner = THIS_MODULE, - .open = acpi_thermal_polling_open_fs, - .read = seq_read, - .write = acpi_thermal_write_polling, - .llseek = seq_lseek, - .release = single_release, -}; -#endif /* CONFIG_ACPI_PROCFS*/ - /* -------------------------------------------------------------------------- Thermal Zone Management -------------------------------------------------------------------------- */ @@ -958,358 +897,6 @@ static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) /* -------------------------------------------------------------------------- - FS Interface (/proc) - -------------------------------------------------------------------------- */ -#ifdef CONFIG_ACPI_PROCFS -static struct proc_dir_entry *acpi_thermal_dir; - -static int acpi_thermal_state_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_thermal *tz = seq->private; - - - if (!tz) - goto end; - - seq_puts(seq, "state: "); - - if (!tz->state.critical && !tz->state.hot && !tz->state.passive - && !tz->state.active) - seq_puts(seq, "ok\n"); - else { - if (tz->state.critical) - seq_puts(seq, "critical "); - if (tz->state.hot) - seq_puts(seq, "hot "); - if (tz->state.passive) - seq_puts(seq, "passive "); - if (tz->state.active) - seq_printf(seq, "active[%d]", tz->state.active_index); - seq_puts(seq, "\n"); - } - - end: - return 0; -} - -static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_thermal_state_seq_show, PDE(inode)->data); -} - -static int acpi_thermal_temp_seq_show(struct seq_file *seq, void *offset) -{ - int result = 0; - struct acpi_thermal *tz = seq->private; - - - if (!tz) - goto end; - - result = acpi_thermal_get_temperature(tz); - if (result) - goto end; - - seq_printf(seq, "temperature: %ld C\n", - KELVIN_TO_CELSIUS(tz->temperature)); - - end: - return 0; -} - -static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_thermal_temp_seq_show, PDE(inode)->data); -} - -static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_thermal *tz = seq->private; - struct acpi_device *device; - acpi_status status; - - int i = 0; - int j = 0; - - - if (!tz) - goto end; - - if (tz->trips.critical.flags.valid) - seq_printf(seq, "critical (S5): %ld C%s", - KELVIN_TO_CELSIUS(tz->trips.critical.temperature), - nocrt ? " <disabled>\n" : "\n"); - - if (tz->trips.hot.flags.valid) - seq_printf(seq, "hot (S4): %ld C%s", - KELVIN_TO_CELSIUS(tz->trips.hot.temperature), - nocrt ? " <disabled>\n" : "\n"); - - if (tz->trips.passive.flags.valid) { - seq_printf(seq, - "passive: %ld C: tc1=%lu tc2=%lu tsp=%lu devices=", - KELVIN_TO_CELSIUS(tz->trips.passive.temperature), - tz->trips.passive.tc1, tz->trips.passive.tc2, - tz->trips.passive.tsp); - for (j = 0; j < tz->trips.passive.devices.count; j++) { - status = acpi_bus_get_device(tz->trips.passive.devices. - handles[j], &device); - seq_printf(seq, "%4.4s ", status ? "" : - acpi_device_bid(device)); - } - seq_puts(seq, "\n"); - } else { - seq_printf(seq, "passive (forced):"); - if (tz->thermal_zone->forced_passive) - seq_printf(seq, " %i C\n", - tz->thermal_zone->forced_passive / 1000); - else - seq_printf(seq, "<not set>\n"); - } - - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - if (!(tz->trips.active[i].flags.valid)) - break; - seq_printf(seq, "active[%d]: %ld C: devices=", - i, - KELVIN_TO_CELSIUS(tz->trips.active[i].temperature)); - for (j = 0; j < tz->trips.active[i].devices.count; j++){ - status = acpi_bus_get_device(tz->trips.active[i]. - devices.handles[j], - &device); - seq_printf(seq, "%4.4s ", status ? "" : - acpi_device_bid(device)); - } - seq_puts(seq, "\n"); - } - - end: - return 0; -} - -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 int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_thermal *tz = seq->private; - - - if (!tz) - goto end; - - if (!tz->flags.cooling_mode) - seq_puts(seq, "<setting not supported>\n"); - else - seq_puts(seq, "0 - Active; 1 - Passive\n"); - - end: - return 0; -} - -static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_thermal_cooling_seq_show, - PDE(inode)->data); -} - -static ssize_t -acpi_thermal_write_cooling_mode(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; - int result = 0; - char mode_string[12] = { '\0' }; - - - if (!tz || (count > sizeof(mode_string) - 1)) - return -EINVAL; - - if (!tz->flags.cooling_mode) - return -ENODEV; - - if (copy_from_user(mode_string, buffer, count)) - return -EFAULT; - - mode_string[count] = '\0'; - - result = acpi_thermal_set_cooling_mode(tz, - simple_strtoul(mode_string, NULL, - 0)); - if (result) - return result; - - acpi_thermal_check(tz); - - return count; -} - -static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_thermal *tz = seq->private; - - - if (!tz) - goto end; - - if (!tz->thermal_zone->polling_delay) { - seq_puts(seq, "<polling disabled>\n"); - goto end; - } - - seq_printf(seq, "polling frequency: %d seconds\n", - (tz->thermal_zone->polling_delay / 1000)); - - end: - return 0; -} - -static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_thermal_polling_seq_show, - PDE(inode)->data); -} - -static int acpi_thermal_set_polling(struct acpi_thermal *tz, int seconds) -{ - if (!tz) - return -EINVAL; - - /* Convert value to deci-seconds */ - tz->polling_frequency = seconds * 10; - - tz->thermal_zone->polling_delay = seconds * 1000; - - if (tz->tz_enabled) - thermal_zone_device_update(tz->thermal_zone); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Polling frequency set to %lu seconds\n", - tz->polling_frequency/10)); - - return 0; -} - -static ssize_t -acpi_thermal_write_polling(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; - int result = 0; - char polling_string[12] = { '\0' }; - int seconds = 0; - - - if (!tz || (count > sizeof(polling_string) - 1)) - return -EINVAL; - - if (copy_from_user(polling_string, buffer, count)) - return -EFAULT; - - polling_string[count] = '\0'; - - seconds = simple_strtoul(polling_string, NULL, 0); - - result = acpi_thermal_set_polling(tz, seconds); - if (result) - return result; - - acpi_thermal_check(tz); - - return count; -} - -static int acpi_thermal_add_fs(struct acpi_device *device) -{ - struct proc_dir_entry *entry = NULL; - - - if (!acpi_device_dir(device)) { - acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), - acpi_thermal_dir); - if (!acpi_device_dir(device)) - return -ENODEV; - } - - /* 'state' [R] */ - entry = proc_create_data(ACPI_THERMAL_FILE_STATE, - S_IRUGO, acpi_device_dir(device), - &acpi_thermal_state_fops, - acpi_driver_data(device)); - if (!entry) - return -ENODEV; - - /* 'temperature' [R] */ - entry = proc_create_data(ACPI_THERMAL_FILE_TEMPERATURE, - S_IRUGO, acpi_device_dir(device), - &acpi_thermal_temp_fops, - acpi_driver_data(device)); - if (!entry) - return -ENODEV; - - /* 'trip_points' [R] */ - entry = proc_create_data(ACPI_THERMAL_FILE_TRIP_POINTS, - S_IRUGO, - acpi_device_dir(device), - &acpi_thermal_trip_fops, - acpi_driver_data(device)); - if (!entry) - return -ENODEV; - - /* 'cooling_mode' [R/W] */ - entry = proc_create_data(ACPI_THERMAL_FILE_COOLING_MODE, - S_IFREG | S_IRUGO | S_IWUSR, - acpi_device_dir(device), - &acpi_thermal_cooling_fops, - acpi_driver_data(device)); - if (!entry) - return -ENODEV; - - /* 'polling_frequency' [R/W] */ - entry = proc_create_data(ACPI_THERMAL_FILE_POLLING_FREQ, - S_IFREG | S_IRUGO | S_IWUSR, - acpi_device_dir(device), - &acpi_thermal_polling_fops, - acpi_driver_data(device)); - if (!entry) - return -ENODEV; - return 0; -} - -static int acpi_thermal_remove_fs(struct acpi_device *device) -{ - - if (acpi_device_dir(device)) { - remove_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ, - acpi_device_dir(device)); - remove_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE, - acpi_device_dir(device)); - remove_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS, - acpi_device_dir(device)); - remove_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE, - acpi_device_dir(device)); - remove_proc_entry(ACPI_THERMAL_FILE_STATE, - acpi_device_dir(device)); - remove_proc_entry(acpi_device_bid(device), acpi_thermal_dir); - acpi_device_dir(device) = NULL; - } - - return 0; -} -#else -static inline int acpi_thermal_add_fs(struct acpi_device *device) { return 0; } -static inline int acpi_thermal_remove_fs(struct acpi_device *device) -{ - return 0; -} -#endif /* CONFIG_ACPI_PROCFS */ -/* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -1428,17 +1015,11 @@ static int acpi_thermal_add(struct acpi_device *device) if (result) goto free_memory; - result = acpi_thermal_add_fs(device); - if (result) - goto unregister_thermal_zone; - printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n", acpi_device_name(device), acpi_device_bid(device), KELVIN_TO_CELSIUS(tz->temperature)); goto end; -unregister_thermal_zone: - thermal_zone_device_unregister(tz->thermal_zone); free_memory: kfree(tz); end: @@ -1454,7 +1035,6 @@ static int acpi_thermal_remove(struct acpi_device *device, int type) tz = acpi_driver_data(device); - acpi_thermal_remove_fs(device); acpi_thermal_unregister_thermal_zone(tz); mutex_destroy(&tz->lock); kfree(tz); @@ -1580,19 +1160,9 @@ static int __init acpi_thermal_init(void) return -ENODEV; } -#ifdef CONFIG_ACPI_PROCFS - acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir); - if (!acpi_thermal_dir) - return -ENODEV; -#endif - result = acpi_bus_register_driver(&acpi_thermal_driver); - if (result < 0) { -#ifdef CONFIG_ACPI_PROCFS - remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir); -#endif + if (result < 0) return -ENODEV; - } return 0; } @@ -1602,10 +1172,6 @@ static void __exit acpi_thermal_exit(void) acpi_bus_unregister_driver(&acpi_thermal_driver); -#ifdef CONFIG_ACPI_PROCFS - remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir); -#endif - return; } diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 67dec0c675a..5cd0228d2da 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -30,8 +30,6 @@ #include <linux/types.h> #include <linux/list.h> #include <linux/mutex.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> #include <linux/input.h> #include <linux/backlight.h> #include <linux/thermal.h> @@ -152,9 +150,6 @@ struct acpi_video_bus { struct acpi_video_bus_flags flags; struct list_head video_device_list; struct mutex device_list_lock; /* protects video_device_list */ -#ifdef CONFIG_ACPI_PROCFS - struct proc_dir_entry *dir; -#endif struct input_dev *input; char phys[32]; /* for input device */ struct notifier_block pm_nb; @@ -210,108 +205,6 @@ struct acpi_video_device { struct output_device *output_dev; }; -#ifdef CONFIG_ACPI_PROCFS -/* bus */ -static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file); -static const struct file_operations acpi_video_bus_info_fops = { - .owner = THIS_MODULE, - .open = acpi_video_bus_info_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file); -static const struct file_operations acpi_video_bus_ROM_fops = { - .owner = THIS_MODULE, - .open = acpi_video_bus_ROM_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int acpi_video_bus_POST_info_open_fs(struct inode *inode, - struct file *file); -static const struct file_operations acpi_video_bus_POST_info_fops = { - .owner = THIS_MODULE, - .open = acpi_video_bus_POST_info_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file); -static ssize_t acpi_video_bus_write_POST(struct file *file, - const char __user *buffer, size_t count, loff_t *data); -static const struct file_operations acpi_video_bus_POST_fops = { - .owner = THIS_MODULE, - .open = acpi_video_bus_POST_open_fs, - .read = seq_read, - .write = acpi_video_bus_write_POST, - .llseek = seq_lseek, - .release = single_release, -}; - -static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file); -static ssize_t acpi_video_bus_write_DOS(struct file *file, - const char __user *buffer, size_t count, loff_t *data); -static const struct file_operations acpi_video_bus_DOS_fops = { - .owner = THIS_MODULE, - .open = acpi_video_bus_DOS_open_fs, - .read = seq_read, - .write = acpi_video_bus_write_DOS, - .llseek = seq_lseek, - .release = single_release, -}; - -/* device */ -static int acpi_video_device_info_open_fs(struct inode *inode, - struct file *file); -static const struct file_operations acpi_video_device_info_fops = { - .owner = THIS_MODULE, - .open = acpi_video_device_info_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int acpi_video_device_state_open_fs(struct inode *inode, - struct file *file); -static ssize_t acpi_video_device_write_state(struct file *file, - const char __user *buffer, size_t count, loff_t *data); -static const struct file_operations acpi_video_device_state_fops = { - .owner = THIS_MODULE, - .open = acpi_video_device_state_open_fs, - .read = seq_read, - .write = acpi_video_device_write_state, - .llseek = seq_lseek, - .release = single_release, -}; - -static int acpi_video_device_brightness_open_fs(struct inode *inode, - struct file *file); -static ssize_t acpi_video_device_write_brightness(struct file *file, - const char __user *buffer, size_t count, loff_t *data); -static const struct file_operations acpi_video_device_brightness_fops = { - .owner = THIS_MODULE, - .open = acpi_video_device_brightness_open_fs, - .read = seq_read, - .write = acpi_video_device_write_brightness, - .llseek = seq_lseek, - .release = single_release, -}; - -static int acpi_video_device_EDID_open_fs(struct inode *inode, - struct file *file); -static const struct file_operations acpi_video_device_EDID_fops = { - .owner = THIS_MODULE, - .open = acpi_video_device_EDID_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif /* CONFIG_ACPI_PROCFS */ - static const char device_decode[][30] = { "motherboard VGA device", "PCI VGA device", @@ -1111,646 +1004,6 @@ static int acpi_video_bus_check(struct acpi_video_bus *video) } /* -------------------------------------------------------------------------- - FS Interface (/proc) - -------------------------------------------------------------------------- */ -#ifdef CONFIG_ACPI_PROCFS - -static struct proc_dir_entry *acpi_video_dir; - -/* video devices */ - -static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_video_device *dev = seq->private; - - - if (!dev) - goto end; - - seq_printf(seq, "device_id: 0x%04x\n", (u32) dev->device_id); - seq_printf(seq, "type: "); - if (dev->flags.crt) - seq_printf(seq, "CRT\n"); - else if (dev->flags.lcd) - seq_printf(seq, "LCD\n"); - else if (dev->flags.tvout) - seq_printf(seq, "TVOUT\n"); - else if (dev->flags.dvi) - seq_printf(seq, "DVI\n"); - else - seq_printf(seq, "UNKNOWN\n"); - - seq_printf(seq, "known by bios: %s\n", dev->flags.bios ? "yes" : "no"); - - end: - return 0; -} - -static int -acpi_video_device_info_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_video_device_info_seq_show, - PDE(inode)->data); -} - -static int -acpi_video_device_query(struct acpi_video_device *device, - unsigned long long *state) -{ - int status; - - status = acpi_evaluate_integer(device->dev->handle, "_DGS", - NULL, state); - - return status; -} - -static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset) -{ - int status; - struct acpi_video_device *dev = seq->private; - unsigned long long state; - - - if (!dev) - goto end; - - status = acpi_video_device_get_state(dev, &state); - seq_printf(seq, "state: "); - if (ACPI_SUCCESS(status)) - seq_printf(seq, "0x%02llx\n", state); - else - seq_printf(seq, "<not supported>\n"); - - status = acpi_video_device_query(dev, &state); - seq_printf(seq, "query: "); - if (ACPI_SUCCESS(status)) - seq_printf(seq, "0x%02llx\n", state); - else - seq_printf(seq, "<not supported>\n"); - - end: - return 0; -} - -static int -acpi_video_device_state_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_video_device_state_seq_show, - PDE(inode)->data); -} - -static ssize_t -acpi_video_device_write_state(struct file *file, - const char __user * buffer, - size_t count, loff_t * data) -{ - int status; - struct seq_file *m = file->private_data; - struct acpi_video_device *dev = m->private; - char str[12] = { 0 }; - u32 state = 0; - - - if (!dev || count >= sizeof(str)) - return -EINVAL; - - if (copy_from_user(str, buffer, count)) - return -EFAULT; - - str[count] = 0; - state = simple_strtoul(str, NULL, 0); - state &= ((1ul << 31) | (1ul << 30) | (1ul << 0)); - - status = acpi_video_device_set_state(dev, state); - - if (status) - return -EFAULT; - - return count; -} - -static int -acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_video_device *dev = seq->private; - int i; - - - if (!dev || !dev->brightness) { - seq_printf(seq, "<not supported>\n"); - return 0; - } - - seq_printf(seq, "levels: "); - for (i = 2; i < dev->brightness->count; i++) - seq_printf(seq, " %d", dev->brightness->levels[i]); - seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr); - - return 0; -} - -static int -acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_video_device_brightness_seq_show, - PDE(inode)->data); -} - -static ssize_t -acpi_video_device_write_brightness(struct file *file, - const char __user * buffer, - size_t count, loff_t * data) -{ - struct seq_file *m = file->private_data; - struct acpi_video_device *dev = m->private; - char str[5] = { 0 }; - unsigned int level = 0; - int i; - - - if (!dev || !dev->brightness || count >= sizeof(str)) - return -EINVAL; - - if (copy_from_user(str, buffer, count)) - return -EFAULT; - - str[count] = 0; - level = simple_strtoul(str, NULL, 0); - - if (level > 100) - return -EFAULT; - - /* validate through the list of available levels */ - for (i = 2; i < dev->brightness->count; i++) - if (level == dev->brightness->levels[i]) { - if (!acpi_video_device_lcd_set_level(dev, level)) - return count; - break; - } - - return -EINVAL; -} - -static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_video_device *dev = seq->private; - int status; - int i; - union acpi_object *edid = NULL; - - - if (!dev) - goto out; - - status = acpi_video_device_EDID(dev, &edid, 128); - if (ACPI_FAILURE(status)) { - status = acpi_video_device_EDID(dev, &edid, 256); - } - - if (ACPI_FAILURE(status)) { - goto out; - } - - if (edid && edid->type == ACPI_TYPE_BUFFER) { - for (i = 0; i < edid->buffer.length; i++) - seq_putc(seq, edid->buffer.pointer[i]); - } - - out: - if (!edid) - seq_printf(seq, "<not supported>\n"); - else - kfree(edid); - - return 0; -} - -static int -acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_video_device_EDID_seq_show, - PDE(inode)->data); -} - -static int acpi_video_device_add_fs(struct acpi_device *device) -{ - struct proc_dir_entry *entry, *device_dir; - struct acpi_video_device *vid_dev; - - vid_dev = acpi_driver_data(device); - if (!vid_dev) - return -ENODEV; - - device_dir = proc_mkdir(acpi_device_bid(device), - vid_dev->video->dir); - if (!device_dir) - return -ENOMEM; - - /* 'info' [R] */ - entry = proc_create_data("info", S_IRUGO, device_dir, - &acpi_video_device_info_fops, acpi_driver_data(device)); - if (!entry) - goto err_remove_dir; - - /* 'state' [R/W] */ - entry = proc_create_data("state", S_IFREG | S_IRUGO | S_IWUSR, - device_dir, - &acpi_video_device_state_fops, - acpi_driver_data(device)); - if (!entry) - goto err_remove_info; - - /* 'brightness' [R/W] */ - entry = proc_create_data("brightness", S_IFREG | S_IRUGO | S_IWUSR, - device_dir, - &acpi_video_device_brightness_fops, - acpi_driver_data(device)); - if (!entry) - goto err_remove_state; - - /* 'EDID' [R] */ - entry = proc_create_data("EDID", S_IRUGO, device_dir, - &acpi_video_device_EDID_fops, - acpi_driver_data(device)); - if (!entry) - goto err_remove_brightness; - - acpi_device_dir(device) = device_dir; - - return 0; - - err_remove_brightness: - remove_proc_entry("brightness", device_dir); - err_remove_state: - remove_proc_entry("state", device_dir); - err_remove_info: - remove_proc_entry("info", device_dir); - err_remove_dir: - remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir); - return -ENOMEM; -} - -static int acpi_video_device_remove_fs(struct acpi_device *device) -{ - struct acpi_video_device *vid_dev; - struct proc_dir_entry *device_dir; - - vid_dev = acpi_driver_data(device); - if (!vid_dev || !vid_dev->video || !vid_dev->video->dir) - return -ENODEV; - - device_dir = acpi_device_dir(device); - if (device_dir) { - remove_proc_entry("info", device_dir); - remove_proc_entry("state", device_dir); - remove_proc_entry("brightness", device_dir); - remove_proc_entry("EDID", device_dir); - remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir); - acpi_device_dir(device) = NULL; - } - - return 0; -} - -/* video bus */ -static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_video_bus *video = seq->private; - - - if (!video) - goto end; - - seq_printf(seq, "Switching heads: %s\n", - video->flags.multihead ? "yes" : "no"); - seq_printf(seq, "Video ROM: %s\n", - video->flags.rom ? "yes" : "no"); - seq_printf(seq, "Device to be POSTed on boot: %s\n", - video->flags.post ? "yes" : "no"); - - end: - return 0; -} - -static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_video_bus_info_seq_show, - PDE(inode)->data); -} - -static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_video_bus *video = seq->private; - - - if (!video) - goto end; - - printk(KERN_INFO PREFIX "Please implement %s\n", __func__); - seq_printf(seq, "<TODO>\n"); - - end: - return 0; -} - -static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data); -} - -static int -acpi_video_bus_POST_options(struct acpi_video_bus *video, - unsigned long long *options) -{ - int status; - - status = acpi_evaluate_integer(video->device->handle, "_VPO", - NULL, options); - *options &= 3; - - return status; -} - -static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_video_bus *video = seq->private; - unsigned long long options; - int status; - - - if (!video) - goto end; - - status = acpi_video_bus_POST_options(video, &options); - if (ACPI_SUCCESS(status)) { - if (!(options & 1)) { - printk(KERN_WARNING PREFIX - "The motherboard VGA device is not listed as a possible POST device.\n"); - printk(KERN_WARNING PREFIX - "This indicates a BIOS bug. Please contact the manufacturer.\n"); - } - printk(KERN_WARNING "%llx\n", options); - seq_printf(seq, "can POST: <integrated video>"); - if (options & 2) - seq_printf(seq, " <PCI video>"); - if (options & 4) - seq_printf(seq, " <AGP video>"); - seq_putc(seq, '\n'); - } else - seq_printf(seq, "<not supported>\n"); - end: - return 0; -} - -static int -acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_video_bus_POST_info_seq_show, - PDE(inode)->data); -} - -static int -acpi_video_bus_get_POST(struct acpi_video_bus *video, unsigned long long *id) -{ - int status; - - status = acpi_evaluate_integer(video->device->handle, "_GPD", NULL, id); - - return status; -} - -static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_video_bus *video = seq->private; - int status; - unsigned long long id; - - - if (!video) - goto end; - - status = acpi_video_bus_get_POST(video, &id); - if (!ACPI_SUCCESS(status)) { - seq_printf(seq, "<not supported>\n"); - goto end; - } - seq_printf(seq, "device POSTed is <%s>\n", device_decode[id & 3]); - - end: - return 0; -} - -static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_video_bus *video = seq->private; - - - seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting); - - return 0; -} - -static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_video_bus_POST_seq_show, - PDE(inode)->data); -} - -static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data); -} - -static int -acpi_video_bus_set_POST(struct acpi_video_bus *video, unsigned long option) -{ - int status; - unsigned long long tmp; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - - - arg0.integer.value = option; - - status = acpi_evaluate_integer(video->device->handle, "_SPD", - &args, &tmp); - if (ACPI_SUCCESS(status)) - status = tmp ? (-EINVAL) : (AE_OK); - - return status; -} - -static ssize_t -acpi_video_bus_write_POST(struct file *file, - const char __user * buffer, - size_t count, loff_t * data) -{ - int status; - struct seq_file *m = file->private_data; - struct acpi_video_bus *video = m->private; - char str[12] = { 0 }; - unsigned long long opt, options; - - - if (!video || count >= sizeof(str)) - return -EINVAL; - - status = acpi_video_bus_POST_options(video, &options); - if (!ACPI_SUCCESS(status)) - return -EINVAL; - - if (copy_from_user(str, buffer, count)) - return -EFAULT; - - str[count] = 0; - opt = strtoul(str, NULL, 0); - if (opt > 3) - return -EFAULT; - - /* just in case an OEM 'forgot' the motherboard... */ - options |= 1; - - if (options & (1ul << opt)) { - status = acpi_video_bus_set_POST(video, opt); - if (!ACPI_SUCCESS(status)) - return -EFAULT; - - } - - return count; -} - -static ssize_t -acpi_video_bus_write_DOS(struct file *file, - const char __user * buffer, - size_t count, loff_t * data) -{ - int status; - struct seq_file *m = file->private_data; - struct acpi_video_bus *video = m->private; - char str[12] = { 0 }; - unsigned long opt; - - - if (!video || count >= sizeof(str)) - return -EINVAL; - - if (copy_from_user(str, buffer, count)) - return -EFAULT; - - str[count] = 0; - opt = strtoul(str, NULL, 0); - if (opt > 7) - return -EFAULT; - - status = acpi_video_bus_DOS(video, opt & 0x3, (opt & 0x4) >> 2); - - if (!ACPI_SUCCESS(status)) - return -EFAULT; - - return count; -} - -static int acpi_video_bus_add_fs(struct acpi_device *device) -{ - struct acpi_video_bus *video = acpi_driver_data(device); - struct proc_dir_entry *device_dir; - struct proc_dir_entry *entry; - - device_dir = proc_mkdir(acpi_device_bid(device), acpi_video_dir); - if (!device_dir) - return -ENOMEM; - - /* 'info' [R] */ - entry = proc_create_data("info", S_IRUGO, device_dir, - &acpi_video_bus_info_fops, - acpi_driver_data(device)); - if (!entry) - goto err_remove_dir; - - /* 'ROM' [R] */ - entry = proc_create_data("ROM", S_IRUGO, device_dir, - &acpi_video_bus_ROM_fops, - acpi_driver_data(device)); - if (!entry) - goto err_remove_info; - - /* 'POST_info' [R] */ - entry = proc_create_data("POST_info", S_IRUGO, device_dir, - &acpi_video_bus_POST_info_fops, - acpi_driver_data(device)); - if (!entry) - goto err_remove_rom; - - /* 'POST' [R/W] */ - entry = proc_create_data("POST", S_IFREG | S_IRUGO | S_IWUSR, - device_dir, - &acpi_video_bus_POST_fops, - acpi_driver_data(device)); - if (!entry) - goto err_remove_post_info; - - /* 'DOS' [R/W] */ - entry = proc_create_data("DOS", S_IFREG | S_IRUGO | S_IWUSR, - device_dir, - &acpi_video_bus_DOS_fops, - acpi_driver_data(device)); - if (!entry) - goto err_remove_post; - - video->dir = acpi_device_dir(device) = device_dir; - return 0; - - err_remove_post: - remove_proc_entry("POST", device_dir); - err_remove_post_info: - remove_proc_entry("POST_info", device_dir); - err_remove_rom: - remove_proc_entry("ROM", device_dir); - err_remove_info: - remove_proc_entry("info", device_dir); - err_remove_dir: - remove_proc_entry(acpi_device_bid(device), acpi_video_dir); - return -ENOMEM; -} - -static int acpi_video_bus_remove_fs(struct acpi_device *device) -{ - struct proc_dir_entry *device_dir = acpi_device_dir(device); - - if (device_dir) { - remove_proc_entry("info", device_dir); - remove_proc_entry("ROM", device_dir); - remove_proc_entry("POST_info", device_dir); - remove_proc_entry("POST", device_dir); - remove_proc_entry("DOS", device_dir); - remove_proc_entry(acpi_device_bid(device), acpi_video_dir); - acpi_device_dir(device) = NULL; - } - - return 0; -} -#else -static inline int acpi_video_device_add_fs(struct acpi_device *device) -{ - return 0; -} -static inline int acpi_video_device_remove_fs(struct acpi_device *device) -{ - return 0; -} -static inline int acpi_video_bus_add_fs(struct acpi_device *device) -{ - return 0; -} -static inline int acpi_video_bus_remove_fs(struct acpi_device *device) -{ - return 0; -} -#endif /* CONFIG_ACPI_PROCFS */ - -/* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -1877,8 +1130,6 @@ acpi_video_bus_get_one_device(struct acpi_device *device, list_add_tail(&data->entry, &video->video_device_list); mutex_unlock(&video->device_list_lock); - acpi_video_device_add_fs(device); - return 0; } @@ -2181,8 +1432,6 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) if (!device || !device->video) return -ENOENT; - acpi_video_device_remove_fs(device->dev); - status = acpi_remove_notify_handler(device->dev->handle, ACPI_DEVICE_NOTIFY, acpi_video_device_notify); @@ -2466,10 +1715,6 @@ static int acpi_video_bus_add(struct acpi_device *device) if (error) goto err_free_video; - error = acpi_video_bus_add_fs(device); - if (error) - goto err_free_video; - mutex_init(&video->device_list_lock); INIT_LIST_HEAD(&video->video_device_list); @@ -2522,7 +1767,6 @@ static int acpi_video_bus_add(struct acpi_device *device) acpi_video_bus_stop_devices(video); acpi_video_bus_put_devices(video); kfree(video->attached_array); - acpi_video_bus_remove_fs(device); err_free_video: kfree(video); device->driver_data = NULL; @@ -2544,7 +1788,6 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type) acpi_video_bus_stop_devices(video); acpi_video_bus_put_devices(video); - acpi_video_bus_remove_fs(device); input_unregister_device(video->input); kfree(video->attached_array); @@ -2584,17 +1827,9 @@ int acpi_video_register(void) return 0; } -#ifdef CONFIG_ACPI_PROCFS - acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir); - if (!acpi_video_dir) - return -ENODEV; -#endif - result = acpi_bus_register_driver(&acpi_video_bus); - if (result < 0) { - remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); + if (result < 0) return -ENODEV; - } /* * When the acpi_video_bus is loaded successfully, increase @@ -2617,10 +1852,6 @@ void acpi_video_unregister(void) } acpi_bus_unregister_driver(&acpi_video_bus); -#ifdef CONFIG_ACPI_PROCFS - remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); -#endif - register_count = 0; return; diff --git a/drivers/base/node.c b/drivers/base/node.c index ee53558b452..ce012a9c620 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -160,6 +160,18 @@ static ssize_t node_read_numastat(struct sys_device * dev, } static SYSDEV_ATTR(numastat, S_IRUGO, node_read_numastat, NULL); +static ssize_t node_read_vmstat(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) +{ + int nid = dev->id; + return sprintf(buf, + "nr_written %lu\n" + "nr_dirtied %lu\n", + node_page_state(nid, NR_WRITTEN), + node_page_state(nid, NR_DIRTIED)); +} +static SYSDEV_ATTR(vmstat, S_IRUGO, node_read_vmstat, NULL); + static ssize_t node_read_distance(struct sys_device * dev, struct sysdev_attribute *attr, char * buf) { @@ -243,6 +255,7 @@ int register_node(struct node *node, int num, struct node *parent) sysdev_create_file(&node->sysdev, &attr_meminfo); sysdev_create_file(&node->sysdev, &attr_numastat); sysdev_create_file(&node->sysdev, &attr_distance); + sysdev_create_file(&node->sysdev, &attr_vmstat); scan_unevictable_register_node(node); @@ -267,6 +280,7 @@ void unregister_node(struct node *node) sysdev_remove_file(&node->sysdev, &attr_meminfo); sysdev_remove_file(&node->sysdev, &attr_numastat); sysdev_remove_file(&node->sysdev, &attr_distance); + sysdev_remove_file(&node->sysdev, &attr_vmstat); scan_unevictable_unregister_node(node); hugetlb_unregister_node(node); /* no-op, if memoryless node */ diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 1dd8676d7f5..126ca492dd0 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -503,7 +503,7 @@ static int rpm_resume(struct device *dev, int rpmflags) * the resume will actually succeed. */ if (dev->power.no_callbacks && !parent && dev->parent) { - spin_lock(&dev->parent->power.lock); + spin_lock_nested(&dev->parent->power.lock, SINGLE_DEPTH_NESTING); if (dev->parent->power.disable_depth > 0 || dev->parent->power.ignore_children || dev->parent->power.runtime_status == RPM_ACTIVE) { diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 6c48b3545f8..450c958b514 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -101,8 +101,8 @@ static int transfer_none(struct loop_device *lo, int cmd, else memcpy(raw_buf, loop_buf, size); - kunmap_atomic(raw_buf, KM_USER0); kunmap_atomic(loop_buf, KM_USER1); + kunmap_atomic(raw_buf, KM_USER0); cond_resched(); return 0; } @@ -130,8 +130,8 @@ static int transfer_xor(struct loop_device *lo, int cmd, for (i = 0; i < size; i++) *out++ = *in++ ^ key[(i & 511) % keysize]; - kunmap_atomic(raw_buf, KM_USER0); kunmap_atomic(loop_buf, KM_USER1); + kunmap_atomic(raw_buf, KM_USER0); cond_resched(); return 0; } diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index a4eee324eb1..55b8667f739 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -32,12 +32,12 @@ #include <linux/bitops.h> #include <linux/compat.h> #include <linux/clocksource.h> +#include <linux/uaccess.h> #include <linux/slab.h> +#include <linux/io.h> #include <asm/current.h> -#include <asm/uaccess.h> #include <asm/system.h> -#include <asm/io.h> #include <asm/irq.h> #include <asm/div64.h> @@ -81,13 +81,13 @@ static cycle_t read_hpet(struct clocksource *cs) } static struct clocksource clocksource_hpet = { - .name = "hpet", - .rating = 250, - .read = read_hpet, - .mask = CLOCKSOURCE_MASK(64), - .mult = 0, /* to be calculated */ - .shift = 10, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .name = "hpet", + .rating = 250, + .read = read_hpet, + .mask = CLOCKSOURCE_MASK(64), + .mult = 0, /* to be calculated */ + .shift = 10, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; static struct clocksource *hpet_clocksource; #endif @@ -465,6 +465,21 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) if (irq) { unsigned long irq_flags; + if (devp->hd_flags & HPET_SHARED_IRQ) { + /* + * To prevent the interrupt handler from seeing an + * unwanted interrupt status bit, program the timer + * so that it will not fire in the near future ... + */ + writel(readl(&timer->hpet_config) & ~Tn_TYPE_CNF_MASK, + &timer->hpet_config); + write_counter(read_counter(&hpet->hpet_mc), + &timer->hpet_compare); + /* ... and clear any left-over status. */ + isr = 1 << (devp - devp->hd_hpets->hp_dev); + writel(isr, &hpet->hpet_isr); + } + sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev)); irq_flags = devp->hd_flags & HPET_SHARED_IRQ ? IRQF_SHARED : IRQF_DISABLED; @@ -581,11 +596,10 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg, break; case HPET_INFO: { + memset(info, 0, sizeof(*info)); if (devp->hd_ireqfreq) info->hi_ireqfreq = hpet_time_div(hpetp, devp->hd_ireqfreq); - else - info->hi_ireqfreq = 0; info->hi_flags = readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK; info->hi_hpet = hpetp->hp_which; @@ -811,7 +825,7 @@ int hpet_alloc(struct hpet_data *hdp) struct hpets *hpetp; size_t siz; struct hpet __iomem *hpet; - static struct hpets *last = NULL; + static struct hpets *last; unsigned long period; unsigned long long temp; u32 remainder; @@ -1000,6 +1014,8 @@ static int hpet_acpi_add(struct acpi_device *device) return -ENODEV; if (!data.hd_address || !data.hd_nirqs) { + if (data.hd_address) + iounmap(data.hd_address); printk("%s: no address or irqs in _CRS\n", __func__); return -ENODEV; } diff --git a/drivers/char/hvc_tile.c b/drivers/char/hvc_tile.c index c4efb55cbc0..7a84a059547 100644 --- a/drivers/char/hvc_tile.c +++ b/drivers/char/hvc_tile.c @@ -61,7 +61,8 @@ console_initcall(hvc_tile_console_init); static int __init hvc_tile_init(void) { - hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128); - return 0; + struct hvc_struct *s; + s = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128); + return IS_ERR(s) ? PTR_ERR(s) : 0; } device_initcall(hvc_tile_init); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index e537610d2f0..b293d57d30a 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1665,6 +1665,17 @@ static int check_hotmod_int_op(const char *curr, const char *option, return 0; } +static struct smi_info *smi_info_alloc(void) +{ + struct smi_info *info = kzalloc(sizeof(*info), GFP_KERNEL); + + if (info) { + spin_lock_init(&info->si_lock); + spin_lock_init(&info->msg_lock); + } + return info; +} + static int hotmod_handler(const char *val, struct kernel_param *kp) { char *str = kstrdup(val, GFP_KERNEL); @@ -1779,7 +1790,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp) } if (op == HM_ADD) { - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) { rv = -ENOMEM; goto out; @@ -1844,7 +1855,7 @@ static __devinit void hardcode_find_bmc(void) if (!ports[i] && !addrs[i]) continue; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) return; @@ -2027,7 +2038,7 @@ static __devinit int try_init_spmi(struct SPMITable *spmi) return -ENODEV; } - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) { printk(KERN_ERR PFX "Could not allocate SI data (3)\n"); return -ENOMEM; @@ -2137,7 +2148,7 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev, if (!acpi_dev) return -ENODEV; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) return -ENOMEM; @@ -2318,7 +2329,7 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) { struct smi_info *info; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) { printk(KERN_ERR PFX "Could not allocate SI data\n"); return; @@ -2425,7 +2436,7 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK; struct smi_info *info; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) return -ENOMEM; @@ -2566,7 +2577,7 @@ static int __devinit ipmi_of_probe(struct platform_device *dev, return -EINVAL; } - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) { dev_err(&dev->dev, @@ -3013,7 +3024,7 @@ static __devinit void default_find_bmc(void) if (check_legacy_ioport(ipmi_defaults[i].port)) continue; #endif - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) return; @@ -3138,9 +3149,6 @@ static int try_smi_init(struct smi_info *new_smi) goto out_err; } - spin_lock_init(&(new_smi->si_lock)); - spin_lock_init(&(new_smi->msg_lock)); - /* Do low-level detection first. */ if (new_smi->handlers->detect(new_smi->si_sm)) { if (new_smi->addr_source) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 199dcb9f0b8..c63a4382374 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -918,8 +918,8 @@ static int cpufreq_add_dev_interface(unsigned int cpu, spin_lock_irqsave(&cpufreq_driver_lock, flags); for_each_cpu(j, policy->cpus) { - if (!cpu_online(j)) - continue; + if (!cpu_online(j)) + continue; per_cpu(cpufreq_cpu_data, j) = policy; per_cpu(cpufreq_policy_cpu, j) = policy->cpu; } diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 7b5093664e4..c631f27a3dc 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -30,6 +30,8 @@ #define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) #define DEF_FREQUENCY_UP_THRESHOLD (80) +#define DEF_SAMPLING_DOWN_FACTOR (1) +#define MAX_SAMPLING_DOWN_FACTOR (100000) #define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) #define MICRO_FREQUENCY_UP_THRESHOLD (95) #define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) @@ -82,6 +84,7 @@ struct cpu_dbs_info_s { unsigned int freq_lo; unsigned int freq_lo_jiffies; unsigned int freq_hi_jiffies; + unsigned int rate_mult; int cpu; unsigned int sample_type:1; /* @@ -108,10 +111,12 @@ static struct dbs_tuners { unsigned int up_threshold; unsigned int down_differential; unsigned int ignore_nice; + unsigned int sampling_down_factor; unsigned int powersave_bias; unsigned int io_is_busy; } dbs_tuners_ins = { .up_threshold = DEF_FREQUENCY_UP_THRESHOLD, + .sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR, .down_differential = DEF_FREQUENCY_DOWN_DIFFERENTIAL, .ignore_nice = 0, .powersave_bias = 0, @@ -259,6 +264,7 @@ static ssize_t show_##file_name \ show_one(sampling_rate, sampling_rate); show_one(io_is_busy, io_is_busy); show_one(up_threshold, up_threshold); +show_one(sampling_down_factor, sampling_down_factor); show_one(ignore_nice_load, ignore_nice); show_one(powersave_bias, powersave_bias); @@ -340,6 +346,29 @@ static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, return count; } +static ssize_t store_sampling_down_factor(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input, j; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1) + return -EINVAL; + mutex_lock(&dbs_mutex); + dbs_tuners_ins.sampling_down_factor = input; + + /* Reset down sampling multiplier in case it was active */ + for_each_online_cpu(j) { + struct cpu_dbs_info_s *dbs_info; + dbs_info = &per_cpu(od_cpu_dbs_info, j); + dbs_info->rate_mult = 1; + } + mutex_unlock(&dbs_mutex); + + return count; +} + static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b, const char *buf, size_t count) { @@ -401,6 +430,7 @@ static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b, define_one_global_rw(sampling_rate); define_one_global_rw(io_is_busy); define_one_global_rw(up_threshold); +define_one_global_rw(sampling_down_factor); define_one_global_rw(ignore_nice_load); define_one_global_rw(powersave_bias); @@ -409,6 +439,7 @@ static struct attribute *dbs_attributes[] = { &sampling_rate_min.attr, &sampling_rate.attr, &up_threshold.attr, + &sampling_down_factor.attr, &ignore_nice_load.attr, &powersave_bias.attr, &io_is_busy.attr, @@ -562,6 +593,10 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) /* Check for frequency increase */ if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) { + /* If switching to max speed, apply sampling_down_factor */ + if (policy->cur < policy->max) + this_dbs_info->rate_mult = + dbs_tuners_ins.sampling_down_factor; dbs_freq_increase(policy, policy->max); return; } @@ -584,6 +619,9 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) (dbs_tuners_ins.up_threshold - dbs_tuners_ins.down_differential); + /* No longer fully busy, reset rate_mult */ + this_dbs_info->rate_mult = 1; + if (freq_next < policy->min) freq_next = policy->min; @@ -607,7 +645,8 @@ static void do_dbs_timer(struct work_struct *work) int sample_type = dbs_info->sample_type; /* We want all CPUs to do sampling nearly on same jiffy */ - int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate + * dbs_info->rate_mult); if (num_online_cpus() > 1) delay -= jiffies % delay; @@ -711,6 +750,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, } } this_dbs_info->cpu = cpu; + this_dbs_info->rate_mult = 1; ondemand_powersave_bias_init_cpu(cpu); /* * Start the timerschedule work, when this governor diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c index 0eac3da566b..a84250a5dd5 100644 --- a/drivers/crypto/hifn_795x.c +++ b/drivers/crypto/hifn_795x.c @@ -1467,7 +1467,7 @@ static int ablkcipher_add(unsigned int *drestp, struct scatterlist *dst, return -EINVAL; while (size) { - copy = min(drest, min(size, dst->length)); + copy = min3(drest, size, dst->length); size -= copy; drest -= copy; @@ -1729,7 +1729,7 @@ static int ablkcipher_get(void *saddr, unsigned int *srestp, unsigned int offset return -EINVAL; while (size) { - copy = min(srest, min(dst->length, size)); + copy = min3(srest, dst->length, size); daddr = kmap_atomic(sg_page(dst), KM_IRQ0); memcpy(daddr + dst->offset + offset, saddr, copy); diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index ce7146677e9..d7ca43a828b 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -42,8 +42,10 @@ #if PAGE_SHIFT < 20 #define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) ) +#define MiB_TO_PAGES(mb) ((mb) >> (20 - PAGE_SHIFT)) #else /* PAGE_SHIFT > 20 */ #define PAGES_TO_MiB( pages ) ( ( pages ) << ( PAGE_SHIFT - 20 ) ) +#define MiB_TO_PAGES(mb) ((mb) >> (PAGE_SHIFT - 20)) #endif #define edac_printk(level, prefix, fmt, arg...) \ @@ -328,7 +330,7 @@ struct csrow_info { struct mcidev_sysfs_group { const char *name; /* group name */ - struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */ + const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */ }; struct mcidev_sysfs_group_kobj { @@ -336,7 +338,7 @@ struct mcidev_sysfs_group_kobj { struct kobject kobj; /* kobj for the group */ - struct mcidev_sysfs_group *grp; /* group description table */ + const struct mcidev_sysfs_group *grp; /* group description table */ struct mem_ctl_info *mci; /* the parent */ }; @@ -347,7 +349,7 @@ struct mcidev_sysfs_group_kobj { struct mcidev_sysfs_attribute { /* It should use either attr or grp */ struct attribute attr; - struct mcidev_sysfs_group *grp; /* Points to a group of attributes */ + const struct mcidev_sysfs_group *grp; /* Points to a group of attributes */ /* Ops for show/store values at the attribute - not used on group */ ssize_t (*show)(struct mem_ctl_info *,char *); @@ -440,7 +442,7 @@ struct mem_ctl_info { * If attributes are desired, then set to array of attributes * If no attributes are desired, leave NULL */ - struct mcidev_sysfs_attribute *mc_driver_sysfs_attributes; + const struct mcidev_sysfs_attribute *mc_driver_sysfs_attributes; /* work struct for this MC */ struct delayed_work work; @@ -810,6 +812,7 @@ extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, extern int edac_mc_add_mc(struct mem_ctl_info *mci); extern void edac_mc_free(struct mem_ctl_info *mci); extern struct mem_ctl_info *edac_mc_find(int idx); +extern struct mem_ctl_info *find_mci_by_dev(struct device *dev); extern struct mem_ctl_info *edac_mc_del_mc(struct device *dev); extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page); diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 6b21e25f7a8..ba6586a69cc 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -207,6 +207,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, } mci->op_state = OP_ALLOC; + INIT_LIST_HEAD(&mci->grp_kobj_list); /* * Initialize the 'root' kobj for the edac_mc controller @@ -234,18 +235,24 @@ EXPORT_SYMBOL_GPL(edac_mc_alloc); */ void edac_mc_free(struct mem_ctl_info *mci) { + debugf1("%s()\n", __func__); + edac_mc_unregister_sysfs_main_kobj(mci); + + /* free the mci instance memory here */ + kfree(mci); } EXPORT_SYMBOL_GPL(edac_mc_free); -/* +/** * find_mci_by_dev * * scan list of controllers looking for the one that manages * the 'dev' device + * @dev: pointer to a struct device related with the MCI */ -static struct mem_ctl_info *find_mci_by_dev(struct device *dev) +struct mem_ctl_info *find_mci_by_dev(struct device *dev) { struct mem_ctl_info *mci; struct list_head *item; @@ -261,6 +268,7 @@ static struct mem_ctl_info *find_mci_by_dev(struct device *dev) return NULL; } +EXPORT_SYMBOL_GPL(find_mci_by_dev); /* * handler for EDAC to check if NMI type handler has asserted interrupt diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index a4135860149..dce61f7ba38 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -631,9 +631,6 @@ static void edac_mci_control_release(struct kobject *kobj) /* decrement the module ref count */ module_put(mci->owner); - - /* free the mci instance memory here */ - kfree(mci); } static struct kobj_type ktype_mci = { @@ -713,6 +710,8 @@ fail_out: */ void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) { + debugf1("%s()\n", __func__); + /* delete the kobj from the mc_kset */ kobject_put(&mci->edac_mci_kobj); } @@ -760,8 +759,6 @@ static void edac_inst_grp_release(struct kobject *kobj) grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj); mci = grp->mci; - - kobject_put(&mci->edac_mci_kobj); } /* Intermediate show/store table */ @@ -784,7 +781,7 @@ static struct kobj_type ktype_inst_grp = { * object tree. */ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, - struct mcidev_sysfs_attribute *sysfs_attrib, + const struct mcidev_sysfs_attribute *sysfs_attrib, struct kobject *kobj) { int err; @@ -792,6 +789,7 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, debugf1("%s()\n", __func__); while (sysfs_attrib) { + debugf1("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); if (sysfs_attrib->grp) { struct mcidev_sysfs_group_kobj *grp_kobj; @@ -799,10 +797,9 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, if (!grp_kobj) return -ENOMEM; - list_add_tail(&grp_kobj->list, &mci->grp_kobj_list); - grp_kobj->grp = sysfs_attrib->grp; grp_kobj->mci = mci; + list_add_tail(&grp_kobj->list, &mci->grp_kobj_list); debugf0("%s() grp %s, mci %p\n", __func__, sysfs_attrib->grp->name, mci); @@ -811,26 +808,28 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, &ktype_inst_grp, &mci->edac_mci_kobj, sysfs_attrib->grp->name); - if (err) + if (err < 0) { + printk(KERN_ERR "kobject_init_and_add failed: %d\n", err); return err; - + } err = edac_create_mci_instance_attributes(mci, grp_kobj->grp->mcidev_attr, &grp_kobj->kobj); - if (err) + if (err < 0) return err; } else if (sysfs_attrib->attr.name) { debugf0("%s() file %s\n", __func__, sysfs_attrib->attr.name); err = sysfs_create_file(kobj, &sysfs_attrib->attr); + if (err < 0) { + printk(KERN_ERR "sysfs_create_file failed: %d\n", err); + return err; + } } else break; - if (err) { - return err; - } sysfs_attrib++; } @@ -843,7 +842,7 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, * directory of this mci instance. */ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, - struct mcidev_sysfs_attribute *sysfs_attrib, + const struct mcidev_sysfs_attribute *sysfs_attrib, struct kobject *kobj, int count) { struct mcidev_sysfs_group_kobj *grp_kobj, *tmp; @@ -855,13 +854,24 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, * Remove first all the atributes */ while (sysfs_attrib) { + debugf1("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); if (sysfs_attrib->grp) { - list_for_each_entry(grp_kobj, &mci->grp_kobj_list, - list) - if (grp_kobj->grp == sysfs_attrib->grp) + debugf1("%s() seeking for group %s\n", + __func__, sysfs_attrib->grp->name); + list_for_each_entry(grp_kobj, + &mci->grp_kobj_list, list) { + debugf1("%s() grp_kobj->grp = %p\n",__func__, grp_kobj->grp); + if (grp_kobj->grp == sysfs_attrib->grp) { edac_remove_mci_instance_attributes(mci, grp_kobj->grp->mcidev_attr, &grp_kobj->kobj, count + 1); + debugf0("%s() group %s\n", __func__, + sysfs_attrib->grp->name); + kobject_put(&grp_kobj->kobj); + } + } + debugf1("%s() end of seeking for group %s\n", + __func__, sysfs_attrib->grp->name); } else if (sysfs_attrib->attr.name) { debugf0("%s() file %s\n", __func__, sysfs_attrib->attr.name); @@ -871,15 +881,14 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, sysfs_attrib++; } - /* - * Now that all attributes got removed, it is save to remove all groups - */ - if (!count) - list_for_each_entry_safe(grp_kobj, tmp, &mci->grp_kobj_list, - list) { - debugf0("%s() grp %s\n", __func__, grp_kobj->grp->name); - kobject_put(&grp_kobj->kobj); - } + /* Remove the group objects */ + if (count) + return; + list_for_each_entry_safe(grp_kobj, tmp, + &mci->grp_kobj_list, list) { + list_del(&grp_kobj->list); + kfree(grp_kobj); + } } @@ -971,6 +980,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) debugf0("%s()\n", __func__); /* remove all csrow kobjects */ + debugf0("%s() unregister this mci kobj\n", __func__); for (i = 0; i < mci->nr_csrows; i++) { if (mci->csrows[i].nr_pages > 0) { debugf0("%s() unreg csrow-%d\n", __func__, i); @@ -978,20 +988,20 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) } } - debugf0("%s() remove_link\n", __func__); + /* remove this mci instance's attribtes */ + if (mci->mc_driver_sysfs_attributes) { + debugf0("%s() unregister mci private attributes\n", __func__); + edac_remove_mci_instance_attributes(mci, + mci->mc_driver_sysfs_attributes, + &mci->edac_mci_kobj, 0); + } /* remove the symlink */ + debugf0("%s() remove_link\n", __func__); sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); - debugf0("%s() remove_mci_instance\n", __func__); - - /* remove this mci instance's attribtes */ - edac_remove_mci_instance_attributes(mci, - mci->mc_driver_sysfs_attributes, - &mci->edac_mci_kobj, 0); - debugf0("%s() unregister this mci kobj\n", __func__); - /* unregister this instance's kobject */ + debugf0("%s() remove_mci_instance\n", __func__); kobject_put(&mci->edac_mci_kobj); } diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 0fd5b85a0f7..362861c1577 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -39,6 +39,14 @@ #include "edac_core.h" +/* Static vars */ +static LIST_HEAD(i7core_edac_list); +static DEFINE_MUTEX(i7core_edac_lock); +static int probed; + +static int use_pci_fixup; +module_param(use_pci_fixup, int, 0444); +MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices"); /* * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core * registers start at bus 255, and are not reported by BIOS. @@ -212,8 +220,8 @@ struct pci_id_descr { }; struct pci_id_table { - struct pci_id_descr *descr; - int n_devs; + const struct pci_id_descr *descr; + int n_devs; }; struct i7core_dev { @@ -235,8 +243,6 @@ struct i7core_pvt { struct i7core_inject inject; struct i7core_channel channel[NUM_CHANS]; - int channels; /* Number of active channels */ - int ce_count_available; int csrow_map[NUM_CHANS][MAX_DIMMS]; @@ -261,22 +267,22 @@ struct i7core_pvt { /* Count indicator to show errors not got */ unsigned mce_overrun; -}; -/* Static vars */ -static LIST_HEAD(i7core_edac_list); -static DEFINE_MUTEX(i7core_edac_lock); + /* Struct to control EDAC polling */ + struct edac_pci_ctl_info *i7core_pci; +}; #define PCI_DESCR(device, function, device_id) \ .dev = (device), \ .func = (function), \ .dev_id = (device_id) -struct pci_id_descr pci_dev_descr_i7core_nehalem[] = { +static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = { /* Memory controller */ { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) }, - /* Exists only for RDIMM */ + + /* Exists only for RDIMM */ { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 }, { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) }, @@ -297,19 +303,9 @@ struct pci_id_descr pci_dev_descr_i7core_nehalem[] = { { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) }, { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) }, { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) }, - - /* Generic Non-core registers */ - /* - * This is the PCI device on i7core and on Xeon 35xx (8086:2c41) - * On Xeon 55xx, however, it has a different id (8086:2c40). So, - * the probing code needs to test for the other address in case of - * failure of this one - */ - { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) }, - }; -struct pci_id_descr pci_dev_descr_lynnfield[] = { +static const struct pci_id_descr pci_dev_descr_lynnfield[] = { { PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) }, { PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) }, { PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) }, @@ -323,15 +319,9 @@ struct pci_id_descr pci_dev_descr_lynnfield[] = { { PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) }, { PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) }, { PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) }, - - /* - * This is the PCI device has an alternate address on some - * processors like Core i7 860 - */ - { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) }, }; -struct pci_id_descr pci_dev_descr_i7core_westmere[] = { +static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = { /* Memory controller */ { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) }, @@ -356,17 +346,14 @@ struct pci_id_descr pci_dev_descr_i7core_westmere[] = { { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) }, { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) }, { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) }, - - /* Generic Non-core registers */ - { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) }, - }; -#define PCI_ID_TABLE_ENTRY(A) { A, ARRAY_SIZE(A) } -struct pci_id_table pci_dev_table[] = { +#define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) } +static const struct pci_id_table pci_dev_table[] = { PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem), PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield), PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere), + {0,} /* 0 terminated list. */ }; /* @@ -378,8 +365,6 @@ static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { {0,} /* 0 terminated list. */ }; -static struct edac_pci_ctl_info *i7core_pci; - /**************************************************************************** Anciliary status routines ****************************************************************************/ @@ -442,6 +427,36 @@ static struct i7core_dev *get_i7core_dev(u8 socket) return NULL; } +static struct i7core_dev *alloc_i7core_dev(u8 socket, + const struct pci_id_table *table) +{ + struct i7core_dev *i7core_dev; + + i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL); + if (!i7core_dev) + return NULL; + + i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * table->n_devs, + GFP_KERNEL); + if (!i7core_dev->pdev) { + kfree(i7core_dev); + return NULL; + } + + i7core_dev->socket = socket; + i7core_dev->n_devs = table->n_devs; + list_add_tail(&i7core_dev->list, &i7core_edac_list); + + return i7core_dev; +} + +static void free_i7core_dev(struct i7core_dev *i7core_dev) +{ + list_del(&i7core_dev->list); + kfree(i7core_dev->pdev); + kfree(i7core_dev); +} + /**************************************************************************** Memory check routines ****************************************************************************/ @@ -484,7 +499,7 @@ static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot, * to add a fake description for csrows. * So, this driver is attributing one DIMM memory for one csrow. */ -static int i7core_get_active_channels(u8 socket, unsigned *channels, +static int i7core_get_active_channels(const u8 socket, unsigned *channels, unsigned *csrows) { struct pci_dev *pdev = NULL; @@ -545,12 +560,13 @@ static int i7core_get_active_channels(u8 socket, unsigned *channels, return 0; } -static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) +static int get_dimm_config(const struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; struct csrow_info *csr; struct pci_dev *pdev; int i, j; + int csrow = 0; unsigned long last_page = 0; enum edac_type mode; enum mem_type mtype; @@ -664,13 +680,9 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) RANKOFFSET(dimm_dod[j]), banks, ranks, rows, cols); -#if PAGE_SHIFT > 20 - npages = size >> (PAGE_SHIFT - 20); -#else - npages = size << (20 - PAGE_SHIFT); -#endif + npages = MiB_TO_PAGES(size); - csr = &mci->csrows[*csrow]; + csr = &mci->csrows[csrow]; csr->first_page = last_page + 1; last_page += npages; csr->last_page = last_page; @@ -678,13 +690,13 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) csr->page_mask = 0; csr->grain = 8; - csr->csrow_idx = *csrow; + csr->csrow_idx = csrow; csr->nr_channels = 1; csr->channels[0].chan_idx = i; csr->channels[0].ce_count = 0; - pvt->csrow_map[i][j] = *csrow; + pvt->csrow_map[i][j] = csrow; switch (banks) { case 4: @@ -703,7 +715,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) csr->edac_mode = mode; csr->mtype = mtype; - (*csrow)++; + csrow++; } pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]); @@ -736,7 +748,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) we're disabling error injection on all write calls to the sysfs nodes that controls the error code injection. */ -static int disable_inject(struct mem_ctl_info *mci) +static int disable_inject(const struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; @@ -921,7 +933,7 @@ DECLARE_ADDR_MATCH(bank, 32); DECLARE_ADDR_MATCH(page, 0x10000); DECLARE_ADDR_MATCH(col, 0x4000); -static int write_and_test(struct pci_dev *dev, int where, u32 val) +static int write_and_test(struct pci_dev *dev, const int where, const u32 val) { u32 read; int count; @@ -1120,35 +1132,34 @@ DECLARE_COUNTER(2); * Sysfs struct */ - -static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = { +static const struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = { ATTR_ADDR_MATCH(channel), ATTR_ADDR_MATCH(dimm), ATTR_ADDR_MATCH(rank), ATTR_ADDR_MATCH(bank), ATTR_ADDR_MATCH(page), ATTR_ADDR_MATCH(col), - { .attr = { .name = NULL } } + { } /* End of list */ }; -static struct mcidev_sysfs_group i7core_inject_addrmatch = { +static const struct mcidev_sysfs_group i7core_inject_addrmatch = { .name = "inject_addrmatch", .mcidev_attr = i7core_addrmatch_attrs, }; -static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = { +static const struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = { ATTR_COUNTER(0), ATTR_COUNTER(1), ATTR_COUNTER(2), { .attr = { .name = NULL } } }; -static struct mcidev_sysfs_group i7core_udimm_counters = { +static const struct mcidev_sysfs_group i7core_udimm_counters = { .name = "all_channel_counts", .mcidev_attr = i7core_udimm_counters_attrs, }; -static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = { +static const struct mcidev_sysfs_attribute i7core_sysfs_rdimm_attrs[] = { { .attr = { .name = "inject_section", @@ -1180,8 +1191,44 @@ static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = { .show = i7core_inject_enable_show, .store = i7core_inject_enable_store, }, - { .attr = { .name = NULL } }, /* Reserved for udimm counters */ - { .attr = { .name = NULL } } + { } /* End of list */ +}; + +static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_attrs[] = { + { + .attr = { + .name = "inject_section", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_inject_section_show, + .store = i7core_inject_section_store, + }, { + .attr = { + .name = "inject_type", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_inject_type_show, + .store = i7core_inject_type_store, + }, { + .attr = { + .name = "inject_eccmask", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_inject_eccmask_show, + .store = i7core_inject_eccmask_store, + }, { + .grp = &i7core_inject_addrmatch, + }, { + .attr = { + .name = "inject_enable", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_inject_enable_show, + .store = i7core_inject_enable_store, + }, { + .grp = &i7core_udimm_counters, + }, + { } /* End of list */ }; /**************************************************************************** @@ -1189,7 +1236,7 @@ static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = { ****************************************************************************/ /* - * i7core_put_devices 'put' all the devices that we have + * i7core_put_all_devices 'put' all the devices that we have * reserved via 'get' */ static void i7core_put_devices(struct i7core_dev *i7core_dev) @@ -1206,23 +1253,23 @@ static void i7core_put_devices(struct i7core_dev *i7core_dev) PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); pci_dev_put(pdev); } - kfree(i7core_dev->pdev); - list_del(&i7core_dev->list); - kfree(i7core_dev); } static void i7core_put_all_devices(void) { struct i7core_dev *i7core_dev, *tmp; - list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) + list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) { i7core_put_devices(i7core_dev); + free_i7core_dev(i7core_dev); + } } -static void __init i7core_xeon_pci_fixup(struct pci_id_table *table) +static void __init i7core_xeon_pci_fixup(const struct pci_id_table *table) { struct pci_dev *pdev = NULL; int i; + /* * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses * aren't announced by acpi. So, we need to use a legacy scan probing @@ -1257,16 +1304,18 @@ static unsigned i7core_pci_lastbus(void) } /* - * i7core_get_devices Find and perform 'get' operation on the MCH's + * i7core_get_all_devices Find and perform 'get' operation on the MCH's * device/functions we want to reference for this driver * * Need to 'get' device 16 func 1 and func 2 */ -int i7core_get_onedevice(struct pci_dev **prev, int devno, - struct pci_id_descr *dev_descr, unsigned n_devs, - unsigned last_bus) +static int i7core_get_onedevice(struct pci_dev **prev, + const struct pci_id_table *table, + const unsigned devno, + const unsigned last_bus) { struct i7core_dev *i7core_dev; + const struct pci_id_descr *dev_descr = &table->descr[devno]; struct pci_dev *pdev = NULL; u8 bus = 0; @@ -1275,20 +1324,6 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, pdev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_descr->dev_id, *prev); - /* - * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs - * is at addr 8086:2c40, instead of 8086:2c41. So, we need - * to probe for the alternate address in case of failure - */ - if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev) - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev); - - if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev) - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT, - *prev); - if (!pdev) { if (*prev) { *prev = pdev; @@ -1315,18 +1350,11 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, i7core_dev = get_i7core_dev(socket); if (!i7core_dev) { - i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL); - if (!i7core_dev) - return -ENOMEM; - i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs, - GFP_KERNEL); - if (!i7core_dev->pdev) { - kfree(i7core_dev); + i7core_dev = alloc_i7core_dev(socket, table); + if (!i7core_dev) { + pci_dev_put(pdev); return -ENOMEM; } - i7core_dev->socket = socket; - i7core_dev->n_devs = n_devs; - list_add_tail(&i7core_dev->list, &i7core_edac_list); } if (i7core_dev->pdev[devno]) { @@ -1368,27 +1396,31 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, dev_descr->func, PCI_VENDOR_ID_INTEL, dev_descr->dev_id); + /* + * As stated on drivers/pci/search.c, the reference count for + * @from is always decremented if it is not %NULL. So, as we need + * to get all devices up to null, we need to do a get for the device + */ + pci_dev_get(pdev); + *prev = pdev; return 0; } -static int i7core_get_devices(struct pci_id_table *table) +static int i7core_get_all_devices(void) { int i, rc, last_bus; struct pci_dev *pdev = NULL; - struct pci_id_descr *dev_descr; + const struct pci_id_table *table = pci_dev_table; last_bus = i7core_pci_lastbus(); while (table && table->descr) { - dev_descr = table->descr; for (i = 0; i < table->n_devs; i++) { pdev = NULL; do { - rc = i7core_get_onedevice(&pdev, i, - &dev_descr[i], - table->n_devs, + rc = i7core_get_onedevice(&pdev, table, i, last_bus); if (rc < 0) { if (i == 0) { @@ -1404,7 +1436,6 @@ static int i7core_get_devices(struct pci_id_table *table) } return 0; - return 0; } static int mci_bind_devs(struct mem_ctl_info *mci, @@ -1414,10 +1445,6 @@ static int mci_bind_devs(struct mem_ctl_info *mci, struct pci_dev *pdev; int i, func, slot; - /* Associates i7core_dev and mci for future usage */ - pvt->i7core_dev = i7core_dev; - i7core_dev->mci = mci; - pvt->is_registered = 0; for (i = 0; i < i7core_dev->n_devs; i++) { pdev = i7core_dev->pdev[i]; @@ -1448,15 +1475,6 @@ static int mci_bind_devs(struct mem_ctl_info *mci, pvt->is_registered = 1; } - /* - * Add extra nodes to count errors on udimm - * For registered memory, this is not needed, since the counters - * are already displayed at the standard locations - */ - if (!pvt->is_registered) - i7core_sysfs_attrs[ARRAY_SIZE(i7core_sysfs_attrs)-2].grp = - &i7core_udimm_counters; - return 0; error: @@ -1470,7 +1488,9 @@ error: Error check routines ****************************************************************************/ static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, - int chan, int dimm, int add) + const int chan, + const int dimm, + const int add) { char *msg; struct i7core_pvt *pvt = mci->pvt_info; @@ -1487,7 +1507,10 @@ static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, } static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci, - int chan, int new0, int new1, int new2) + const int chan, + const int new0, + const int new1, + const int new2) { struct i7core_pvt *pvt = mci->pvt_info; int add0 = 0, add1 = 0, add2 = 0; @@ -1641,7 +1664,7 @@ static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci) * fields */ static void i7core_mce_output_error(struct mem_ctl_info *mci, - struct mce *m) + const struct mce *m) { struct i7core_pvt *pvt = mci->pvt_info; char *type, *optype, *err, *msg; @@ -1845,28 +1868,85 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) return 1; } -static int i7core_register_mci(struct i7core_dev *i7core_dev, - int num_channels, int num_csrows) +static void i7core_pci_ctl_create(struct i7core_pvt *pvt) +{ + pvt->i7core_pci = edac_pci_create_generic_ctl( + &pvt->i7core_dev->pdev[0]->dev, + EDAC_MOD_STR); + if (unlikely(!pvt->i7core_pci)) + pr_warn("Unable to setup PCI error report via EDAC\n"); +} + +static void i7core_pci_ctl_release(struct i7core_pvt *pvt) +{ + if (likely(pvt->i7core_pci)) + edac_pci_release_generic_ctl(pvt->i7core_pci); + else + i7core_printk(KERN_ERR, + "Couldn't find mem_ctl_info for socket %d\n", + pvt->i7core_dev->socket); + pvt->i7core_pci = NULL; +} + +static void i7core_unregister_mci(struct i7core_dev *i7core_dev) +{ + struct mem_ctl_info *mci = i7core_dev->mci; + struct i7core_pvt *pvt; + + if (unlikely(!mci || !mci->pvt_info)) { + debugf0("MC: " __FILE__ ": %s(): dev = %p\n", + __func__, &i7core_dev->pdev[0]->dev); + + i7core_printk(KERN_ERR, "Couldn't find mci handler\n"); + return; + } + + pvt = mci->pvt_info; + + debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n", + __func__, mci, &i7core_dev->pdev[0]->dev); + + /* Disable MCE NMI handler */ + edac_mce_unregister(&pvt->edac_mce); + + /* Disable EDAC polling */ + i7core_pci_ctl_release(pvt); + + /* Remove MC sysfs nodes */ + edac_mc_del_mc(mci->dev); + + debugf1("%s: free mci struct\n", mci->ctl_name); + kfree(mci->ctl_name); + edac_mc_free(mci); + i7core_dev->mci = NULL; +} + +static int i7core_register_mci(struct i7core_dev *i7core_dev) { struct mem_ctl_info *mci; struct i7core_pvt *pvt; - int csrow = 0; - int rc; + int rc, channels, csrows; + + /* Check the number of active and not disabled channels */ + rc = i7core_get_active_channels(i7core_dev->socket, &channels, &csrows); + if (unlikely(rc < 0)) + return rc; /* allocate a new MC control structure */ - mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, - i7core_dev->socket); + mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, i7core_dev->socket); if (unlikely(!mci)) return -ENOMEM; - debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); - - /* record ptr to the generic device */ - mci->dev = &i7core_dev->pdev[0]->dev; + debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n", + __func__, mci, &i7core_dev->pdev[0]->dev); pvt = mci->pvt_info; memset(pvt, 0, sizeof(*pvt)); + /* Associates i7core_dev and mci for future usage */ + pvt->i7core_dev = i7core_dev; + i7core_dev->mci = mci; + /* * FIXME: how to handle RDDR3 at MCI level? It is possible to have * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different @@ -1881,17 +1961,23 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev, i7core_dev->socket); mci->dev_name = pci_name(i7core_dev->pdev[0]); mci->ctl_page_to_phys = NULL; - mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs; - /* Set the function pointer to an actual operation function */ - mci->edac_check = i7core_check_error; /* Store pci devices at mci for faster access */ rc = mci_bind_devs(mci, i7core_dev); if (unlikely(rc < 0)) - goto fail; + goto fail0; + + if (pvt->is_registered) + mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs; + else + mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs; /* Get dimm basic config */ - get_dimm_config(mci, &csrow); + get_dimm_config(mci); + /* record ptr to the generic device */ + mci->dev = &i7core_dev->pdev[0]->dev; + /* Set the function pointer to an actual operation function */ + mci->edac_check = i7core_check_error; /* add this new MC control structure to EDAC's list of MCs */ if (unlikely(edac_mc_add_mc(mci))) { @@ -1902,19 +1988,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev, */ rc = -EINVAL; - goto fail; - } - - /* allocating generic PCI control info */ - i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev, - EDAC_MOD_STR); - if (unlikely(!i7core_pci)) { - printk(KERN_WARNING - "%s(): Unable to create PCI control\n", - __func__); - printk(KERN_WARNING - "%s(): PCI error report via EDAC not setup\n", - __func__); + goto fail0; } /* Default error mask is any memory */ @@ -1925,19 +1999,28 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev, pvt->inject.page = -1; pvt->inject.col = -1; + /* allocating generic PCI control info */ + i7core_pci_ctl_create(pvt); + /* Registers on edac_mce in order to receive memory errors */ pvt->edac_mce.priv = mci; pvt->edac_mce.check_error = i7core_mce_check_error; - rc = edac_mce_register(&pvt->edac_mce); if (unlikely(rc < 0)) { debugf0("MC: " __FILE__ ": %s(): failed edac_mce_register()\n", __func__); + goto fail1; } -fail: - if (rc < 0) - edac_mc_free(mci); + return 0; + +fail1: + i7core_pci_ctl_release(pvt); + edac_mc_del_mc(mci->dev); +fail0: + kfree(mci->ctl_name); + edac_mc_free(mci); + i7core_dev->mci = NULL; return rc; } @@ -1949,8 +2032,6 @@ fail: * < 0 for error code */ -static int probed = 0; - static int __devinit i7core_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1965,25 +2046,16 @@ static int __devinit i7core_probe(struct pci_dev *pdev, */ if (unlikely(probed >= 1)) { mutex_unlock(&i7core_edac_lock); - return -EINVAL; + return -ENODEV; } probed++; - rc = i7core_get_devices(pci_dev_table); + rc = i7core_get_all_devices(); if (unlikely(rc < 0)) goto fail0; list_for_each_entry(i7core_dev, &i7core_edac_list, list) { - int channels; - int csrows; - - /* Check the number of active and not disabled channels */ - rc = i7core_get_active_channels(i7core_dev->socket, - &channels, &csrows); - if (unlikely(rc < 0)) - goto fail1; - - rc = i7core_register_mci(i7core_dev, channels, csrows); + rc = i7core_register_mci(i7core_dev); if (unlikely(rc < 0)) goto fail1; } @@ -1994,6 +2066,9 @@ static int __devinit i7core_probe(struct pci_dev *pdev, return 0; fail1: + list_for_each_entry(i7core_dev, &i7core_edac_list, list) + i7core_unregister_mci(i7core_dev); + i7core_put_all_devices(); fail0: mutex_unlock(&i7core_edac_lock); @@ -2006,14 +2081,10 @@ fail0: */ static void __devexit i7core_remove(struct pci_dev *pdev) { - struct mem_ctl_info *mci; - struct i7core_dev *i7core_dev, *tmp; + struct i7core_dev *i7core_dev; debugf0(__FILE__ ": %s()\n", __func__); - if (i7core_pci) - edac_pci_release_generic_ctl(i7core_pci); - /* * we have a trouble here: pdev value for removal will be wrong, since * it will point to the X58 register used to detect that the machine @@ -2023,22 +2094,18 @@ static void __devexit i7core_remove(struct pci_dev *pdev) */ mutex_lock(&i7core_edac_lock); - list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) { - mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev); - if (mci) { - struct i7core_pvt *pvt = mci->pvt_info; - - i7core_dev = pvt->i7core_dev; - edac_mce_unregister(&pvt->edac_mce); - kfree(mci->ctl_name); - edac_mc_free(mci); - i7core_put_devices(i7core_dev); - } else { - i7core_printk(KERN_ERR, - "Couldn't find mci for socket %d\n", - i7core_dev->socket); - } + + if (unlikely(!probed)) { + mutex_unlock(&i7core_edac_lock); + return; } + + list_for_each_entry(i7core_dev, &i7core_edac_list, list) + i7core_unregister_mci(i7core_dev); + + /* Release PCI resources */ + i7core_put_all_devices(); + probed--; mutex_unlock(&i7core_edac_lock); @@ -2070,7 +2137,8 @@ static int __init i7core_init(void) /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); - i7core_xeon_pci_fixup(pci_dev_table); + if (use_pci_fixup) + i7core_xeon_pci_fixup(pci_dev_table); pci_rc = pci_register_driver(&i7core_driver); diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index a2b12aa1f2b..501866662e0 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -345,7 +345,7 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid) do { level = __ffs(pending); - handle_nested_irq(level + chip->irq_base); + generic_handle_irq(level + chip->irq_base); pending &= ~(1 << level); } while (pending); @@ -360,7 +360,8 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, struct pca953x_platform_data *pdata = client->dev.platform_data; int ret; - if (pdata->irq_base && (id->driver_data & PCA953X_INT)) { + if (pdata->irq_base != -1 + && (id->driver_data & PCA953X_INT)) { int lvl; ret = pca953x_read_reg(chip, PCA953X_INPUT, @@ -383,7 +384,6 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, set_irq_chip_data(irq, chip); set_irq_chip_and_handler(irq, &pca953x_irq_chip, handle_edge_irq); - set_irq_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else @@ -394,6 +394,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, ret = request_threaded_irq(client->irq, NULL, pca953x_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, dev_name(&client->dev), chip); if (ret) { @@ -408,13 +409,13 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, return 0; out_failed: - chip->irq_base = 0; + chip->irq_base = -1; return ret; } static void pca953x_irq_teardown(struct pca953x_chip *chip) { - if (chip->irq_base) + if (chip->irq_base != -1) free_irq(chip->client->irq, chip); } #else /* CONFIG_GPIO_PCA953X_IRQ */ @@ -424,7 +425,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, struct i2c_client *client = chip->client; struct pca953x_platform_data *pdata = client->dev.platform_data; - if (pdata->irq_base && (id->driver_data & PCA953X_INT)) + if (pdata->irq_base != -1 && (id->driver_data & PCA953X_INT)) dev_warn(&client->dev, "interrupt support not compiled in\n"); return 0; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 90b1d6753b9..eb6c473c6d1 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -155,11 +155,11 @@ fast_shmem_read(struct page **pages, char __iomem *vaddr; int unwritten; - vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT], KM_USER0); + vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT]); if (vaddr == NULL) return -ENOMEM; unwritten = __copy_to_user_inatomic(data, vaddr + page_offset, length); - kunmap_atomic(vaddr, KM_USER0); + kunmap_atomic(vaddr); if (unwritten) return -EFAULT; @@ -509,10 +509,10 @@ fast_user_write(struct io_mapping *mapping, char *vaddr_atomic; unsigned long unwritten; - vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base, KM_USER0); + vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base); unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + page_offset, user_data, length); - io_mapping_unmap_atomic(vaddr_atomic, KM_USER0); + io_mapping_unmap_atomic(vaddr_atomic); if (unwritten) return -EFAULT; return 0; @@ -551,11 +551,11 @@ fast_shmem_write(struct page **pages, char __iomem *vaddr; unsigned long unwritten; - vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT], KM_USER0); + vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT]); if (vaddr == NULL) return -ENOMEM; unwritten = __copy_from_user_inatomic(vaddr + page_offset, data, length); - kunmap_atomic(vaddr, KM_USER0); + kunmap_atomic(vaddr); if (unwritten) return -EFAULT; @@ -3346,8 +3346,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, reloc_offset = obj_priv->gtt_offset + reloc->offset; reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, (reloc_offset & - ~(PAGE_SIZE - 1)), - KM_USER0); + ~(PAGE_SIZE - 1))); reloc_entry = (uint32_t __iomem *)(reloc_page + (reloc_offset & (PAGE_SIZE - 1))); reloc_val = target_obj_priv->gtt_offset + reloc->delta; @@ -3358,7 +3357,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, readl(reloc_entry), reloc_val); #endif writel(reloc_val, reloc_entry); - io_mapping_unmap_atomic(reloc_page, KM_USER0); + io_mapping_unmap_atomic(reloc_page); /* The updated presumed offset for this entry will be * copied back out to the user. @@ -4772,11 +4771,11 @@ void i915_gem_detach_phys_object(struct drm_device *dev, page_count = obj->size / PAGE_SIZE; for (i = 0; i < page_count; i++) { - char *dst = kmap_atomic(obj_priv->pages[i], KM_USER0); + char *dst = kmap_atomic(obj_priv->pages[i]); char *src = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE); memcpy(dst, src, PAGE_SIZE); - kunmap_atomic(dst, KM_USER0); + kunmap_atomic(dst); } drm_clflush_pages(obj_priv->pages, page_count); drm_agp_chipset_flush(dev); @@ -4833,11 +4832,11 @@ i915_gem_attach_phys_object(struct drm_device *dev, page_count = obj->size / PAGE_SIZE; for (i = 0; i < page_count; i++) { - char *src = kmap_atomic(obj_priv->pages[i], KM_USER0); + char *src = kmap_atomic(obj_priv->pages[i]); char *dst = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE); memcpy(dst, src, PAGE_SIZE); - kunmap_atomic(src, KM_USER0); + kunmap_atomic(src); } i915_gem_object_put_pages(obj); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 744225ebb4b..b80010f0c4c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -456,10 +456,9 @@ i915_error_object_create(struct drm_device *dev, local_irq_save(flags); s = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, - reloc_offset, - KM_IRQ0); + reloc_offset); memcpy_fromio(d, s, PAGE_SIZE); - io_mapping_unmap_atomic(s, KM_IRQ0); + io_mapping_unmap_atomic(s); local_irq_restore(flags); dst->pages[page] = d; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 1d306a458be..3264bbd47e6 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -187,8 +187,7 @@ static struct overlay_registers *intel_overlay_map_regs_atomic(struct intel_over if (OVERLAY_NONPHYSICAL(overlay->dev)) { regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, - overlay->reg_bo->gtt_offset, - KM_USER0); + overlay->reg_bo->gtt_offset); if (!regs) { DRM_ERROR("failed to map overlay regs in GTT\n"); @@ -203,7 +202,7 @@ static struct overlay_registers *intel_overlay_map_regs_atomic(struct intel_over static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay) { if (OVERLAY_NONPHYSICAL(overlay->dev)) - io_mapping_unmap_atomic(overlay->virt_addr, KM_USER0); + io_mapping_unmap_atomic(overlay->virt_addr); overlay->virt_addr = NULL; diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 974b0f8ae04..8fa339600fe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -2167,11 +2167,11 @@ peek_fb(struct drm_device *dev, struct io_mapping *fb, if (off < pci_resource_len(dev->pdev, 1)) { uint8_t __iomem *p = - io_mapping_map_atomic_wc(fb, off & PAGE_MASK, KM_USER0); + io_mapping_map_atomic_wc(fb, off & PAGE_MASK); val = ioread32(p + (off & ~PAGE_MASK)); - io_mapping_unmap_atomic(p, KM_USER0); + io_mapping_unmap_atomic(p); } return val; @@ -2183,12 +2183,12 @@ poke_fb(struct drm_device *dev, struct io_mapping *fb, { if (off < pci_resource_len(dev->pdev, 1)) { uint8_t __iomem *p = - io_mapping_map_atomic_wc(fb, off & PAGE_MASK, KM_USER0); + io_mapping_map_atomic_wc(fb, off & PAGE_MASK); iowrite32(val, p + (off & ~PAGE_MASK)); wmb(); - io_mapping_unmap_atomic(p, KM_USER0); + io_mapping_unmap_atomic(p); } } diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 3451a82adba..e8a73e65da6 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -170,7 +170,7 @@ static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src, src = (void *)((unsigned long)src + (page << PAGE_SHIFT)); #ifdef CONFIG_X86 - dst = kmap_atomic_prot(d, KM_USER0, prot); + dst = kmap_atomic_prot(d, prot); #else if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) dst = vmap(&d, 1, 0, prot); @@ -183,7 +183,7 @@ static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src, memcpy_fromio(dst, src, PAGE_SIZE); #ifdef CONFIG_X86 - kunmap_atomic(dst, KM_USER0); + kunmap_atomic(dst); #else if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) vunmap(dst); @@ -206,7 +206,7 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst, dst = (void *)((unsigned long)dst + (page << PAGE_SHIFT)); #ifdef CONFIG_X86 - src = kmap_atomic_prot(s, KM_USER0, prot); + src = kmap_atomic_prot(s, prot); #else if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) src = vmap(&s, 1, 0, prot); @@ -219,7 +219,7 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst, memcpy_toio(dst, src, PAGE_SIZE); #ifdef CONFIG_X86 - kunmap_atomic(src, KM_USER0); + kunmap_atomic(src); #else if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) vunmap(src); diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e382da3122b..c357c835eb1 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -399,6 +399,15 @@ config SENSORS_GL520SM This driver can also be built as a module. If so, the module will be called gl520sm. +config SENSORS_GPIO_FAN + tristate "GPIO fan" + depends on GENERIC_GPIO + help + If you say yes here you get support for fans connected to GPIO lines. + + This driver can also be built as a module. If so, the module + will be called gpio-fan. + config SENSORS_CORETEMP tristate "Intel Core/Core2/Atom temperature sensor" depends on X86 && PCI && EXPERIMENTAL @@ -654,6 +663,17 @@ config SENSORS_LTC4245 This driver can also be built as a module. If so, the module will be called ltc4245. +config SENSORS_LTC4261 + tristate "Linear Technology LTC4261" + depends on I2C && EXPERIMENTAL + default n + help + If you say yes here you get support for Linear Technology LTC4261 + Negative Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4261. + config SENSORS_LM95241 tristate "National Semiconductor LM95241 sensor chip" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index ec9cb735c89..d30f0f6870e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o obj-$(CONFIG_SENSORS_G760A) += g760a.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o @@ -79,6 +80,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o +obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index a23b17a78ac..42de98d73ff 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -21,7 +21,6 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> @@ -280,11 +279,9 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id, case 0x1a: dev_warn(dev, "TjMax is assumed as 100 C!\n"); return 100000; - break; case 0x17: case 0x1c: /* Atom CPUs */ return adjust_tjmax(c, id, dev); - break; default: dev_warn(dev, "CPU (model=0x%x) is not supported yet," " using default TjMax of 100C.\n", c->x86_model); @@ -292,6 +289,15 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id, } } +static void __devinit get_ucode_rev_on_cpu(void *edx) +{ + u32 eax; + + wrmsr(MSR_IA32_UCODE_REV, 0, 0); + sync_core(); + rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx); +} + static int __devinit coretemp_probe(struct platform_device *pdev) { struct coretemp_data *data; @@ -327,8 +333,15 @@ static int __devinit coretemp_probe(struct platform_device *pdev) if ((c->x86_model == 0xe) && (c->x86_mask < 0xc)) { /* check for microcode update */ - rdmsr_on_cpu(data->id, MSR_IA32_UCODE_REV, &eax, &edx); - if (edx < 0x39) { + err = smp_call_function_single(data->id, get_ucode_rev_on_cpu, + &edx, 1); + if (err) { + dev_err(&pdev->dev, + "Cannot determine microcode revision of " + "CPU#%u (%d)!\n", data->id, err); + err = -ENODEV; + goto exit_free; + } else if (edx < 0x39) { err = -ENODEV; dev_err(&pdev->dev, "Errata AE18 not fixed, update BIOS or " @@ -490,7 +503,7 @@ exit: return err; } -static void coretemp_device_remove(unsigned int cpu) +static void __cpuinit coretemp_device_remove(unsigned int cpu) { struct pdev_entry *p; unsigned int i; @@ -569,9 +582,8 @@ exit: static void __exit coretemp_exit(void) { struct pdev_entry *p, *n; -#ifdef CONFIG_HOTPLUG_CPU + unregister_hotcpu_notifier(&coretemp_cpu_notifier); -#endif mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { platform_device_unregister(p->pdev); diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c new file mode 100644 index 00000000000..aa701a18370 --- /dev/null +++ b/drivers/hwmon/gpio-fan.c @@ -0,0 +1,558 @@ +/* + * gpio-fan.c - Hwmon driver for fans connected to GPIO lines. + * + * Copyright (C) 2010 LaCie + * + * Author: Simon Guinot <sguinot@lacie.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/hwmon.h> +#include <linux/gpio.h> +#include <linux/gpio-fan.h> + +struct gpio_fan_data { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex lock; /* lock GPIOs operations. */ + int num_ctrl; + unsigned *ctrl; + int num_speed; + struct gpio_fan_speed *speed; + int speed_index; +#ifdef CONFIG_PM + int resume_speed; +#endif + bool pwm_enable; + struct gpio_fan_alarm *alarm; + struct work_struct alarm_work; +}; + +/* + * Alarm GPIO. + */ + +static void fan_alarm_notify(struct work_struct *ws) +{ + struct gpio_fan_data *fan_data = + container_of(ws, struct gpio_fan_data, alarm_work); + + sysfs_notify(&fan_data->pdev->dev.kobj, NULL, "fan1_alarm"); + kobject_uevent(&fan_data->pdev->dev.kobj, KOBJ_CHANGE); +} + +static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id) +{ + struct gpio_fan_data *fan_data = dev_id; + + schedule_work(&fan_data->alarm_work); + + return IRQ_NONE; +} + +static ssize_t show_fan_alarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + struct gpio_fan_alarm *alarm = fan_data->alarm; + int value = gpio_get_value(alarm->gpio); + + if (alarm->active_low) + value = !value; + + return sprintf(buf, "%d\n", value); +} + +static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL); + +static int fan_alarm_init(struct gpio_fan_data *fan_data, + struct gpio_fan_alarm *alarm) +{ + int err; + int alarm_irq; + struct platform_device *pdev = fan_data->pdev; + + fan_data->alarm = alarm; + + err = gpio_request(alarm->gpio, "GPIO fan alarm"); + if (err) + return err; + + err = gpio_direction_input(alarm->gpio); + if (err) + goto err_free_gpio; + + err = device_create_file(&pdev->dev, &dev_attr_fan1_alarm); + if (err) + goto err_free_gpio; + + /* + * If the alarm GPIO don't support interrupts, just leave + * without initializing the fail notification support. + */ + alarm_irq = gpio_to_irq(alarm->gpio); + if (alarm_irq < 0) + return 0; + + INIT_WORK(&fan_data->alarm_work, fan_alarm_notify); + set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); + err = request_irq(alarm_irq, fan_alarm_irq_handler, IRQF_SHARED, + "GPIO fan alarm", fan_data); + if (err) + goto err_free_sysfs; + + return 0; + +err_free_sysfs: + device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); +err_free_gpio: + gpio_free(alarm->gpio); + + return err; +} + +static void fan_alarm_free(struct gpio_fan_data *fan_data) +{ + struct platform_device *pdev = fan_data->pdev; + int alarm_irq = gpio_to_irq(fan_data->alarm->gpio); + + if (alarm_irq >= 0) + free_irq(alarm_irq, fan_data); + device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); + gpio_free(fan_data->alarm->gpio); +} + +/* + * Control GPIOs. + */ + +/* Must be called with fan_data->lock held, except during initialization. */ +static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val) +{ + int i; + + for (i = 0; i < fan_data->num_ctrl; i++) + gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1); +} + +static int __get_fan_ctrl(struct gpio_fan_data *fan_data) +{ + int i; + int ctrl_val = 0; + + for (i = 0; i < fan_data->num_ctrl; i++) { + int value; + + value = gpio_get_value(fan_data->ctrl[i]); + ctrl_val |= (value << i); + } + return ctrl_val; +} + +/* Must be called with fan_data->lock held, except during initialization. */ +static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index) +{ + if (fan_data->speed_index == speed_index) + return; + + __set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val); + fan_data->speed_index = speed_index; +} + +static int get_fan_speed_index(struct gpio_fan_data *fan_data) +{ + int ctrl_val = __get_fan_ctrl(fan_data); + int i; + + for (i = 0; i < fan_data->num_speed; i++) + if (fan_data->speed[i].ctrl_val == ctrl_val) + return i; + + dev_warn(&fan_data->pdev->dev, + "missing speed array entry for GPIO value 0x%x\n", ctrl_val); + + return -EINVAL; +} + +static int rpm_to_speed_index(struct gpio_fan_data *fan_data, int rpm) +{ + struct gpio_fan_speed *speed = fan_data->speed; + int i; + + for (i = 0; i < fan_data->num_speed; i++) + if (speed[i].rpm >= rpm) + return i; + + return fan_data->num_speed - 1; +} + +static ssize_t show_pwm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1); + + return sprintf(buf, "%d\n", pwm); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + unsigned long pwm; + int speed_index; + int ret = count; + + if (strict_strtoul(buf, 10, &pwm) || pwm > 255) + return -EINVAL; + + mutex_lock(&fan_data->lock); + + if (!fan_data->pwm_enable) { + ret = -EPERM; + goto exit_unlock; + } + + speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255); + set_fan_speed(fan_data, speed_index); + +exit_unlock: + mutex_unlock(&fan_data->lock); + + return ret; +} + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", fan_data->pwm_enable); +} + +static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) || val > 1) + return -EINVAL; + + if (fan_data->pwm_enable == val) + return count; + + mutex_lock(&fan_data->lock); + + fan_data->pwm_enable = val; + + /* Disable manual control mode: set fan at full speed. */ + if (val == 0) + set_fan_speed(fan_data, fan_data->num_speed - 1); + + mutex_unlock(&fan_data->lock); + + return count; +} + +static ssize_t show_pwm_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0\n"); +} + +static ssize_t show_rpm_min(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", fan_data->speed[0].rpm); +} + +static ssize_t show_rpm_max(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", + fan_data->speed[fan_data->num_speed - 1].rpm); +} + +static ssize_t show_rpm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", fan_data->speed[fan_data->speed_index].rpm); +} + +static ssize_t set_rpm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + unsigned long rpm; + int ret = count; + + if (strict_strtoul(buf, 10, &rpm)) + return -EINVAL; + + mutex_lock(&fan_data->lock); + + if (!fan_data->pwm_enable) { + ret = -EPERM; + goto exit_unlock; + } + + set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm)); + +exit_unlock: + mutex_unlock(&fan_data->lock); + + return ret; +} + +static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm); +static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, + show_pwm_enable, set_pwm_enable); +static DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL); +static DEVICE_ATTR(fan1_min, S_IRUGO, show_rpm_min, NULL); +static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL); +static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL); +static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm); + +static struct attribute *gpio_fan_ctrl_attributes[] = { + &dev_attr_pwm1.attr, + &dev_attr_pwm1_enable.attr, + &dev_attr_pwm1_mode.attr, + &dev_attr_fan1_input.attr, + &dev_attr_fan1_target.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan1_max.attr, + NULL +}; + +static const struct attribute_group gpio_fan_ctrl_group = { + .attrs = gpio_fan_ctrl_attributes, +}; + +static int fan_ctrl_init(struct gpio_fan_data *fan_data, + struct gpio_fan_platform_data *pdata) +{ + struct platform_device *pdev = fan_data->pdev; + int num_ctrl = pdata->num_ctrl; + unsigned *ctrl = pdata->ctrl; + int i, err; + + for (i = 0; i < num_ctrl; i++) { + err = gpio_request(ctrl[i], "GPIO fan control"); + if (err) + goto err_free_gpio; + + err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i])); + if (err) { + gpio_free(ctrl[i]); + goto err_free_gpio; + } + } + + err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); + if (err) + goto err_free_gpio; + + fan_data->num_ctrl = num_ctrl; + fan_data->ctrl = ctrl; + fan_data->num_speed = pdata->num_speed; + fan_data->speed = pdata->speed; + fan_data->pwm_enable = true; /* Enable manual fan speed control. */ + fan_data->speed_index = get_fan_speed_index(fan_data); + if (fan_data->speed_index < 0) { + err = -ENODEV; + goto err_free_gpio; + } + + return 0; + +err_free_gpio: + for (i = i - 1; i >= 0; i--) + gpio_free(ctrl[i]); + + return err; +} + +static void fan_ctrl_free(struct gpio_fan_data *fan_data) +{ + struct platform_device *pdev = fan_data->pdev; + int i; + + sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); + for (i = 0; i < fan_data->num_ctrl; i++) + gpio_free(fan_data->ctrl[i]); +} + +/* + * Platform driver. + */ + +static ssize_t show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "gpio-fan\n"); +} + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static int __devinit gpio_fan_probe(struct platform_device *pdev) +{ + int err; + struct gpio_fan_data *fan_data; + struct gpio_fan_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return -EINVAL; + + fan_data = kzalloc(sizeof(struct gpio_fan_data), GFP_KERNEL); + if (!fan_data) + return -ENOMEM; + + fan_data->pdev = pdev; + platform_set_drvdata(pdev, fan_data); + mutex_init(&fan_data->lock); + + /* Configure alarm GPIO if available. */ + if (pdata->alarm) { + err = fan_alarm_init(fan_data, pdata->alarm); + if (err) + goto err_free_data; + } + + /* Configure control GPIOs if available. */ + if (pdata->ctrl && pdata->num_ctrl > 0) { + if (!pdata->speed || pdata->num_speed <= 1) { + err = -EINVAL; + goto err_free_alarm; + } + err = fan_ctrl_init(fan_data, pdata); + if (err) + goto err_free_alarm; + } + + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + goto err_free_ctrl; + + /* Make this driver part of hwmon class. */ + fan_data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(fan_data->hwmon_dev)) { + err = PTR_ERR(fan_data->hwmon_dev); + goto err_remove_name; + } + + dev_info(&pdev->dev, "GPIO fan initialized\n"); + + return 0; + +err_remove_name: + device_remove_file(&pdev->dev, &dev_attr_name); +err_free_ctrl: + if (fan_data->ctrl) + fan_ctrl_free(fan_data); +err_free_alarm: + if (fan_data->alarm) + fan_alarm_free(fan_data); +err_free_data: + platform_set_drvdata(pdev, NULL); + kfree(fan_data); + + return err; +} + +static int __devexit gpio_fan_remove(struct platform_device *pdev) +{ + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + hwmon_device_unregister(fan_data->hwmon_dev); + device_remove_file(&pdev->dev, &dev_attr_name); + if (fan_data->alarm) + fan_alarm_free(fan_data); + if (fan_data->ctrl) + fan_ctrl_free(fan_data); + kfree(fan_data); + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_fan_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + if (fan_data->ctrl) { + fan_data->resume_speed = fan_data->speed_index; + set_fan_speed(fan_data, 0); + } + + return 0; +} + +static int gpio_fan_resume(struct platform_device *pdev) +{ + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + if (fan_data->ctrl) + set_fan_speed(fan_data, fan_data->resume_speed); + + return 0; +} +#else +#define gpio_fan_suspend NULL +#define gpio_fan_resume NULL +#endif + +static struct platform_driver gpio_fan_driver = { + .probe = gpio_fan_probe, + .remove = __devexit_p(gpio_fan_remove), + .suspend = gpio_fan_suspend, + .resume = gpio_fan_resume, + .driver = { + .name = "gpio-fan", + }, +}; + +static int __init gpio_fan_init(void) +{ + return platform_driver_register(&gpio_fan_driver); +} + +static void __exit gpio_fan_exit(void) +{ + platform_driver_unregister(&gpio_fan_driver); +} + +module_init(gpio_fan_init); +module_exit(gpio_fan_exit); + +MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); +MODULE_DESCRIPTION("GPIO FAN driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-fan"); diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index 36e95753223..a56a78412fc 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -146,7 +146,7 @@ int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) { - lis3_dev.ac = *((struct axis_conversion *)dmi->driver_data); + lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); return 1; @@ -154,16 +154,19 @@ static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) /* Represents, for each axis seen by userspace, the corresponding hw axis (+1). * If the value is negative, the opposite of the hw value is used. */ -static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; -static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; -static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; -static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; -static struct axis_conversion lis3lv02d_axis_xy_swap = {2, 1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_rotated_left_usd = {-2, 1, -3}; -static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_rotated_right = {2, -1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_swap_yz_inverted = {2, -1, -3}; +#define DEFINE_CONV(name, x, y, z) \ + static union axis_conversion lis3lv02d_axis_##name = \ + { .as_array = { x, y, z } } +DEFINE_CONV(normal, 1, 2, 3); +DEFINE_CONV(y_inverted, 1, -2, 3); +DEFINE_CONV(x_inverted, -1, 2, 3); +DEFINE_CONV(z_inverted, 1, 2, -3); +DEFINE_CONV(xy_swap, 2, 1, 3); +DEFINE_CONV(xy_rotated_left, -2, 1, 3); +DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); +DEFINE_CONV(xy_swap_inverted, -2, -1, 3); +DEFINE_CONV(xy_rotated_right, 2, -1, 3); +DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); #define AXIS_DMI_MATCH(_ident, _name, _axis) { \ .ident = _ident, \ @@ -222,7 +225,7 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), - AXIS_DMI_MATCH("Mini5102", "HP Mini 5102", xy_rotated_left_usd), + AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), { NULL, } /* Laptop models without axis info (yet): * "NC6910" "HP Compaq 6910" @@ -299,7 +302,10 @@ static int lis3lv02d_add(struct acpi_device *device) lis3lv02d_enum_resources(device); /* If possible use a "standard" axes order */ - if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { + if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { + printk(KERN_INFO DRIVER_NAME ": Using custom axes %d,%d,%d\n", + lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); + } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " "using default axes configuration\n"); lis3_dev.ac = lis3lv02d_axis_normal; diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index fc591ae5310..0cee73a6124 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -31,9 +31,11 @@ #include <linux/delay.h> #include <linux/wait.h> #include <linux/poll.h> +#include <linux/slab.h> #include <linux/freezer.h> #include <linux/uaccess.h> #include <linux/miscdevice.h> +#include <linux/pm_runtime.h> #include <asm/atomic.h> #include "lis3lv02d.h" @@ -43,6 +45,16 @@ #define MDPS_POLL_INTERVAL 50 #define MDPS_POLL_MIN 0 #define MDPS_POLL_MAX 2000 + +#define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */ + +#define SELFTEST_OK 0 +#define SELFTEST_FAIL -1 +#define SELFTEST_IRQ -2 + +#define IRQ_LINE0 0 +#define IRQ_LINE1 1 + /* * The sensor can also generate interrupts (DRDY) but it's pretty pointless * because they are generated even if the data do not change. So it's better @@ -66,8 +78,10 @@ #define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024) #define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY) -#define LIS3_DEFAULT_FUZZ 3 -#define LIS3_DEFAULT_FLAT 3 +#define LIS3_DEFAULT_FUZZ_12B 3 +#define LIS3_DEFAULT_FLAT_12B 3 +#define LIS3_DEFAULT_FUZZ_8B 1 +#define LIS3_DEFAULT_FLAT_8B 1 struct lis3lv02d lis3_dev = { .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait), @@ -75,6 +89,30 @@ struct lis3lv02d lis3_dev = { EXPORT_SYMBOL_GPL(lis3_dev); +/* just like param_set_int() but does sanity-check so that it won't point + * over the axis array size + */ +static int param_set_axis(const char *val, const struct kernel_param *kp) +{ + int ret = param_set_int(val, kp); + if (!ret) { + int val = *(int *)kp->arg; + if (val < 0) + val = -val; + if (!val || val > 3) + return -EINVAL; + } + return ret; +} + +static struct kernel_param_ops param_ops_axis = { + .set = param_set_axis, + .get = param_get_int, +}; + +module_param_array_named(axes, lis3_dev.ac.as_array, axis, NULL, 0644); +MODULE_PARM_DESC(axes, "Axis-mapping for x,y,z directions"); + static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg) { s8 lo; @@ -123,9 +161,24 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z) int position[3]; int i; - position[0] = lis3->read_data(lis3, OUTX); - position[1] = lis3->read_data(lis3, OUTY); - position[2] = lis3->read_data(lis3, OUTZ); + if (lis3->blkread) { + if (lis3_dev.whoami == WAI_12B) { + u16 data[3]; + lis3->blkread(lis3, OUTX_L, 6, (u8 *)data); + for (i = 0; i < 3; i++) + position[i] = (s16)le16_to_cpu(data[i]); + } else { + u8 data[5]; + /* Data: x, dummy, y, dummy, z */ + lis3->blkread(lis3, OUTX, 5, data); + for (i = 0; i < 3; i++) + position[i] = (s8)data[i * 2]; + } + } else { + position[0] = lis3->read_data(lis3, OUTX); + position[1] = lis3->read_data(lis3, OUTY); + position[2] = lis3->read_data(lis3, OUTZ); + } for (i = 0; i < 3; i++) position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY; @@ -138,6 +191,7 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z) /* conversion btw sampling rate and the register values */ static int lis3_12_rates[4] = {40, 160, 640, 2560}; static int lis3_8_rates[2] = {100, 400}; +static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000}; /* ODR is Output Data Rate */ static int lis3lv02d_get_odr(void) @@ -156,6 +210,9 @@ static int lis3lv02d_set_odr(int rate) u8 ctrl; int i, len, shift; + if (!rate) + return -EINVAL; + lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl); ctrl &= ~lis3_dev.odr_mask; len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */ @@ -172,19 +229,42 @@ static int lis3lv02d_set_odr(int rate) static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) { - u8 reg; + u8 ctlreg, reg; s16 x, y, z; u8 selftest; int ret; + u8 ctrl_reg_data; + unsigned char irq_cfg; mutex_lock(&lis3->mutex); - if (lis3_dev.whoami == WAI_12B) - selftest = CTRL1_ST; - else - selftest = CTRL1_STP; - lis3->read(lis3, CTRL_REG1, ®); - lis3->write(lis3, CTRL_REG1, (reg | selftest)); + irq_cfg = lis3->irq_cfg; + if (lis3_dev.whoami == WAI_8B) { + lis3->data_ready_count[IRQ_LINE0] = 0; + lis3->data_ready_count[IRQ_LINE1] = 0; + + /* Change interrupt cfg to data ready for selftest */ + atomic_inc(&lis3_dev.wake_thread); + lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY; + lis3->read(lis3, CTRL_REG3, &ctrl_reg_data); + lis3->write(lis3, CTRL_REG3, (ctrl_reg_data & + ~(LIS3_IRQ1_MASK | LIS3_IRQ2_MASK)) | + (LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY)); + } + + if (lis3_dev.whoami == WAI_3DC) { + ctlreg = CTRL_REG4; + selftest = CTRL4_ST0; + } else { + ctlreg = CTRL_REG1; + if (lis3_dev.whoami == WAI_12B) + selftest = CTRL1_ST; + else + selftest = CTRL1_STP; + } + + lis3->read(lis3, ctlreg, ®); + lis3->write(lis3, ctlreg, (reg | selftest)); msleep(lis3->pwron_delay / lis3lv02d_get_odr()); /* Read directly to avoid axis remap */ @@ -193,7 +273,7 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) z = lis3->read_data(lis3, OUTZ); /* back to normal settings */ - lis3->write(lis3, CTRL_REG1, reg); + lis3->write(lis3, ctlreg, reg); msleep(lis3->pwron_delay / lis3lv02d_get_odr()); results[0] = x - lis3->read_data(lis3, OUTX); @@ -201,13 +281,33 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) results[2] = z - lis3->read_data(lis3, OUTZ); ret = 0; + + if (lis3_dev.whoami == WAI_8B) { + /* Restore original interrupt configuration */ + atomic_dec(&lis3_dev.wake_thread); + lis3->write(lis3, CTRL_REG3, ctrl_reg_data); + lis3->irq_cfg = irq_cfg; + + if ((irq_cfg & LIS3_IRQ1_MASK) && + lis3->data_ready_count[IRQ_LINE0] < 2) { + ret = SELFTEST_IRQ; + goto fail; + } + + if ((irq_cfg & LIS3_IRQ2_MASK) && + lis3->data_ready_count[IRQ_LINE1] < 2) { + ret = SELFTEST_IRQ; + goto fail; + } + } + if (lis3->pdata) { int i; for (i = 0; i < 3; i++) { /* Check against selftest acceptance limits */ if ((results[i] < lis3->pdata->st_min_limits[i]) || (results[i] > lis3->pdata->st_max_limits[i])) { - ret = -EIO; + ret = SELFTEST_FAIL; goto fail; } } @@ -219,10 +319,46 @@ fail: return ret; } +/* + * Order of registers in the list affects to order of the restore process. + * Perhaps it is a good idea to set interrupt enable register as a last one + * after all other configurations + */ +static u8 lis3_wai8_regs[] = { FF_WU_CFG_1, FF_WU_THS_1, FF_WU_DURATION_1, + FF_WU_CFG_2, FF_WU_THS_2, FF_WU_DURATION_2, + CLICK_CFG, CLICK_SRC, CLICK_THSY_X, CLICK_THSZ, + CLICK_TIMELIMIT, CLICK_LATENCY, CLICK_WINDOW, + CTRL_REG1, CTRL_REG2, CTRL_REG3}; + +static u8 lis3_wai12_regs[] = {FF_WU_CFG, FF_WU_THS_L, FF_WU_THS_H, + FF_WU_DURATION, DD_CFG, DD_THSI_L, DD_THSI_H, + DD_THSE_L, DD_THSE_H, + CTRL_REG1, CTRL_REG3, CTRL_REG2}; + +static inline void lis3_context_save(struct lis3lv02d *lis3) +{ + int i; + for (i = 0; i < lis3->regs_size; i++) + lis3->read(lis3, lis3->regs[i], &lis3->reg_cache[i]); + lis3->regs_stored = true; +} + +static inline void lis3_context_restore(struct lis3lv02d *lis3) +{ + int i; + if (lis3->regs_stored) + for (i = 0; i < lis3->regs_size; i++) + lis3->write(lis3, lis3->regs[i], lis3->reg_cache[i]); +} + void lis3lv02d_poweroff(struct lis3lv02d *lis3) { + if (lis3->reg_ctrl) + lis3_context_save(lis3); /* disable X,Y,Z axis and power down */ lis3->write(lis3, CTRL_REG1, 0x00); + if (lis3->reg_ctrl) + lis3->reg_ctrl(lis3, LIS3_REG_OFF); } EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); @@ -232,19 +368,24 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3) lis3->init(lis3); - /* LIS3 power on delay is quite long */ - msleep(lis3->pwron_delay / lis3lv02d_get_odr()); - /* * Common configuration * BDU: (12 bits sensors only) LSB and MSB values are not updated until * both have been read. So the value read will always be correct. + * Set BOOT bit to refresh factory tuning values. */ - if (lis3->whoami == WAI_12B) { - lis3->read(lis3, CTRL_REG2, ®); - reg |= CTRL2_BDU; - lis3->write(lis3, CTRL_REG2, reg); - } + lis3->read(lis3, CTRL_REG2, ®); + if (lis3->whoami == WAI_12B) + reg |= CTRL2_BDU | CTRL2_BOOT; + else + reg |= CTRL2_BOOT_8B; + lis3->write(lis3, CTRL_REG2, reg); + + /* LIS3 power on delay is quite long */ + msleep(lis3->pwron_delay / lis3lv02d_get_odr()); + + if (lis3->reg_ctrl) + lis3_context_restore(lis3); } EXPORT_SYMBOL_GPL(lis3lv02d_poweron); @@ -262,6 +403,27 @@ static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev) mutex_unlock(&lis3_dev.mutex); } +static void lis3lv02d_joystick_open(struct input_polled_dev *pidev) +{ + if (lis3_dev.pm_dev) + pm_runtime_get_sync(lis3_dev.pm_dev); + + if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev) + atomic_set(&lis3_dev.wake_thread, 1); + /* + * Update coordinates for the case where poll interval is 0 and + * the chip in running purely under interrupt control + */ + lis3lv02d_joystick_poll(pidev); +} + +static void lis3lv02d_joystick_close(struct input_polled_dev *pidev) +{ + atomic_set(&lis3_dev.wake_thread, 0); + if (lis3_dev.pm_dev) + pm_runtime_put(lis3_dev.pm_dev); +} + static irqreturn_t lis302dl_interrupt(int irq, void *dummy) { if (!test_bit(0, &lis3_dev.misc_opened)) @@ -277,8 +439,7 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy) wake_up_interruptible(&lis3_dev.misc_wait); kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); out: - if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev && - lis3_dev.idev->input->users) + if (atomic_read(&lis3_dev.wake_thread)) return IRQ_WAKE_THREAD; return IRQ_HANDLED; } @@ -309,44 +470,41 @@ static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3) mutex_unlock(&lis3->mutex); } -static void lis302dl_interrupt_handle_ff_wu(struct lis3lv02d *lis3) +static inline void lis302dl_data_ready(struct lis3lv02d *lis3, int index) { - u8 wu1_src; - u8 wu2_src; - - lis3->read(lis3, FF_WU_SRC_1, &wu1_src); - lis3->read(lis3, FF_WU_SRC_2, &wu2_src); + int dummy; - wu1_src = wu1_src & FF_WU_SRC_IA ? wu1_src : 0; - wu2_src = wu2_src & FF_WU_SRC_IA ? wu2_src : 0; - - /* joystick poll is internally protected by the lis3->mutex. */ - if (wu1_src || wu2_src) - lis3lv02d_joystick_poll(lis3_dev.idev); + /* Dummy read to ack interrupt */ + lis3lv02d_get_xyz(lis3, &dummy, &dummy, &dummy); + lis3->data_ready_count[index]++; } static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data) { - struct lis3lv02d *lis3 = data; + u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ1_MASK; - if ((lis3->pdata->irq_cfg & LIS3_IRQ1_MASK) == LIS3_IRQ1_CLICK) + if (irq_cfg == LIS3_IRQ1_CLICK) lis302dl_interrupt_handle_click(lis3); + else if (unlikely(irq_cfg == LIS3_IRQ1_DATA_READY)) + lis302dl_data_ready(lis3, IRQ_LINE0); else - lis302dl_interrupt_handle_ff_wu(lis3); + lis3lv02d_joystick_poll(lis3->idev); return IRQ_HANDLED; } static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data) { - struct lis3lv02d *lis3 = data; + u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ2_MASK; - if ((lis3->pdata->irq_cfg & LIS3_IRQ2_MASK) == LIS3_IRQ2_CLICK) + if (irq_cfg == LIS3_IRQ2_CLICK) lis302dl_interrupt_handle_click(lis3); + else if (unlikely(irq_cfg == LIS3_IRQ2_DATA_READY)) + lis302dl_data_ready(lis3, IRQ_LINE1); else - lis302dl_interrupt_handle_ff_wu(lis3); + lis3lv02d_joystick_poll(lis3->idev); return IRQ_HANDLED; } @@ -356,6 +514,9 @@ static int lis3lv02d_misc_open(struct inode *inode, struct file *file) if (test_and_set_bit(0, &lis3_dev.misc_opened)) return -EBUSY; /* already open */ + if (lis3_dev.pm_dev) + pm_runtime_get_sync(lis3_dev.pm_dev); + atomic_set(&lis3_dev.count, 0); return 0; } @@ -364,6 +525,8 @@ static int lis3lv02d_misc_release(struct inode *inode, struct file *file) { fasync_helper(-1, file, 0, &lis3_dev.async_queue); clear_bit(0, &lis3_dev.misc_opened); /* release the device */ + if (lis3_dev.pm_dev) + pm_runtime_put(lis3_dev.pm_dev); return 0; } @@ -460,6 +623,8 @@ int lis3lv02d_joystick_enable(void) return -ENOMEM; lis3_dev.idev->poll = lis3lv02d_joystick_poll; + lis3_dev.idev->open = lis3lv02d_joystick_open; + lis3_dev.idev->close = lis3lv02d_joystick_close; lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL; lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN; lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX; @@ -473,8 +638,16 @@ int lis3lv02d_joystick_enable(void) set_bit(EV_ABS, input_dev->evbit); max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY; - fuzz = (LIS3_DEFAULT_FUZZ * lis3_dev.scale) / LIS3_ACCURACY; - flat = (LIS3_DEFAULT_FLAT * lis3_dev.scale) / LIS3_ACCURACY; + if (lis3_dev.whoami == WAI_12B) { + fuzz = LIS3_DEFAULT_FUZZ_12B; + flat = LIS3_DEFAULT_FLAT_12B; + } else { + fuzz = LIS3_DEFAULT_FUZZ_8B; + flat = LIS3_DEFAULT_FLAT_8B; + } + fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY; + flat = (flat * lis3_dev.scale) / LIS3_ACCURACY; + input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat); input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat); input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat); @@ -512,14 +685,47 @@ void lis3lv02d_joystick_disable(void) EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); /* Sysfs stuff */ +static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3) +{ + /* + * SYSFS functions are fast visitors so put-call + * immediately after the get-call. However, keep + * chip running for a while and schedule delayed + * suspend. This way periodic sysfs calls doesn't + * suffer from relatively long power up time. + */ + + if (lis3->pm_dev) { + pm_runtime_get_sync(lis3->pm_dev); + pm_runtime_put_noidle(lis3->pm_dev); + pm_schedule_suspend(lis3->pm_dev, LIS3_SYSFS_POWERDOWN_DELAY); + } +} + static ssize_t lis3lv02d_selftest_show(struct device *dev, struct device_attribute *attr, char *buf) { - int result; s16 values[3]; - result = lis3lv02d_selftest(&lis3_dev, values); - return sprintf(buf, "%s %d %d %d\n", result == 0 ? "OK" : "FAIL", + static const char ok[] = "OK"; + static const char fail[] = "FAIL"; + static const char irq[] = "FAIL_IRQ"; + const char *res; + + lis3lv02d_sysfs_poweron(&lis3_dev); + switch (lis3lv02d_selftest(&lis3_dev, values)) { + case SELFTEST_FAIL: + res = fail; + break; + case SELFTEST_IRQ: + res = irq; + break; + case SELFTEST_OK: + default: + res = ok; + break; + } + return sprintf(buf, "%s %d %d %d\n", res, values[0], values[1], values[2]); } @@ -528,6 +734,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev, { int x, y, z; + lis3lv02d_sysfs_poweron(&lis3_dev); mutex_lock(&lis3_dev.mutex); lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); mutex_unlock(&lis3_dev.mutex); @@ -537,6 +744,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev, static ssize_t lis3lv02d_rate_show(struct device *dev, struct device_attribute *attr, char *buf) { + lis3lv02d_sysfs_poweron(&lis3_dev); return sprintf(buf, "%d\n", lis3lv02d_get_odr()); } @@ -549,6 +757,7 @@ static ssize_t lis3lv02d_rate_set(struct device *dev, if (strict_strtoul(buf, 0, &rate)) return -EINVAL; + lis3lv02d_sysfs_poweron(&lis3_dev); if (lis3lv02d_set_odr(rate)) return -EINVAL; @@ -585,6 +794,18 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3) { sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); platform_device_unregister(lis3->pdev); + if (lis3->pm_dev) { + /* Barrier after the sysfs remove */ + pm_runtime_barrier(lis3->pm_dev); + + /* SYSFS may have left chip running. Turn off if necessary */ + if (!pm_runtime_suspended(lis3->pm_dev)) + lis3lv02d_poweroff(&lis3_dev); + + pm_runtime_disable(lis3->pm_dev); + pm_runtime_set_suspended(lis3->pm_dev); + } + kfree(lis3->reg_cache); return 0; } EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); @@ -616,16 +837,16 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev, if (p->wakeup_flags) { dev->write(dev, FF_WU_CFG_1, p->wakeup_flags); dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f); - /* default to 2.5ms for now */ - dev->write(dev, FF_WU_DURATION_1, 1); + /* pdata value + 1 to keep this backward compatible*/ + dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1); ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/ } if (p->wakeup_flags2) { dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2); dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f); - /* default to 2.5ms for now */ - dev->write(dev, FF_WU_DURATION_2, 1); + /* pdata value + 1 to keep this backward compatible*/ + dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1); ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/ } /* Configure hipass filters */ @@ -635,8 +856,8 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev, err = request_threaded_irq(p->irq2, NULL, lis302dl_interrupt_thread2_8b, - IRQF_TRIGGER_RISING | - IRQF_ONESHOT, + IRQF_TRIGGER_RISING | IRQF_ONESHOT | + (p->irq_flags2 & IRQF_TRIGGER_MASK), DRIVER_NAME, &lis3_dev); if (err < 0) printk(KERN_ERR DRIVER_NAME @@ -652,6 +873,7 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) { int err; irq_handler_t thread_fn; + int irq_flags = 0; dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I); @@ -664,6 +886,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) dev->odrs = lis3_12_rates; dev->odr_mask = CTRL1_DF0 | CTRL1_DF1; dev->scale = LIS3_SENSITIVITY_12B; + dev->regs = lis3_wai12_regs; + dev->regs_size = ARRAY_SIZE(lis3_wai12_regs); break; case WAI_8B: printk(KERN_INFO DRIVER_NAME ": 8 bits sensor found\n"); @@ -673,6 +897,17 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) dev->odrs = lis3_8_rates; dev->odr_mask = CTRL1_DR; dev->scale = LIS3_SENSITIVITY_8B; + dev->regs = lis3_wai8_regs; + dev->regs_size = ARRAY_SIZE(lis3_wai8_regs); + break; + case WAI_3DC: + printk(KERN_INFO DRIVER_NAME ": 8 bits 3DC sensor found\n"); + dev->read_data = lis3lv02d_read_8; + dev->mdps_max_val = 128; + dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B; + dev->odrs = lis3_3dc_rates; + dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3; + dev->scale = LIS3_SENSITIVITY_8B; break; default: printk(KERN_ERR DRIVER_NAME @@ -680,11 +915,25 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) return -EINVAL; } + dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs), + sizeof(lis3_wai12_regs)), GFP_KERNEL); + + if (dev->reg_cache == NULL) { + printk(KERN_ERR DRIVER_NAME "out of memory\n"); + return -ENOMEM; + } + mutex_init(&dev->mutex); + atomic_set(&dev->wake_thread, 0); lis3lv02d_add_fs(dev); lis3lv02d_poweron(dev); + if (dev->pm_dev) { + pm_runtime_set_active(dev->pm_dev); + pm_runtime_enable(dev->pm_dev); + } + if (lis3lv02d_joystick_enable()) printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); @@ -696,8 +945,14 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) if (dev->whoami == WAI_8B) lis3lv02d_8b_configure(dev, p); + irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK; + + dev->irq_cfg = p->irq_cfg; if (p->irq_cfg) dev->write(dev, CTRL_REG3, p->irq_cfg); + + if (p->default_rate) + lis3lv02d_set_odr(p->default_rate); } /* bail if we did not get an IRQ from the bus layer */ @@ -725,7 +980,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) err = request_threaded_irq(dev->irq, lis302dl_interrupt, thread_fn, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, + IRQF_TRIGGER_RISING | IRQF_ONESHOT | + irq_flags, DRIVER_NAME, &lis3_dev); if (err < 0) { diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 854091380e3..a1939589eb2 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -20,6 +20,7 @@ */ #include <linux/platform_device.h> #include <linux/input-polldev.h> +#include <linux/regulator/consumer.h> /* * This driver tries to support the "digital" accelerometer chips from @@ -45,6 +46,7 @@ enum lis3_reg { CTRL_REG1 = 0x20, CTRL_REG2 = 0x21, CTRL_REG3 = 0x22, + CTRL_REG4 = 0x23, HP_FILTER_RESET = 0x23, STATUS_REG = 0x27, OUTX_L = 0x28, @@ -93,6 +95,7 @@ enum lis3lv02d_reg { }; enum lis3_who_am_i { + WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */ WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */ WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */ WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */ @@ -118,6 +121,13 @@ enum lis3lv02d_ctrl1_8b { CTRL1_DR = 0x80, }; +enum lis3lv02d_ctrl1_3dc { + CTRL1_ODR0 = 0x10, + CTRL1_ODR1 = 0x20, + CTRL1_ODR2 = 0x40, + CTRL1_ODR3 = 0x80, +}; + enum lis3lv02d_ctrl2 { CTRL2_DAS = 0x01, CTRL2_SIM = 0x02, @@ -129,9 +139,18 @@ enum lis3lv02d_ctrl2 { CTRL2_FS = 0x80, /* Full Scale selection */ }; +enum lis3lv02d_ctrl4_3dc { + CTRL4_SIM = 0x01, + CTRL4_ST0 = 0x02, + CTRL4_ST1 = 0x04, + CTRL4_FS0 = 0x10, + CTRL4_FS1 = 0x20, +}; + enum lis302d_ctrl2 { HP_FF_WU2 = 0x08, HP_FF_WU1 = 0x04, + CTRL2_BOOT_8B = 0x40, }; enum lis3lv02d_ctrl3 { @@ -206,19 +225,33 @@ enum lis3lv02d_click_src_8b { CLICK_IA = 0x40, }; -struct axis_conversion { - s8 x; - s8 y; - s8 z; +enum lis3lv02d_reg_state { + LIS3_REG_OFF = 0x00, + LIS3_REG_ON = 0x01, +}; + +union axis_conversion { + struct { + int x, y, z; + }; + int as_array[3]; + }; struct lis3lv02d { void *bus_priv; /* used by the bus layer only */ + struct device *pm_dev; /* for pm_runtime purposes */ int (*init) (struct lis3lv02d *lis3); int (*write) (struct lis3lv02d *lis3, int reg, u8 val); int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); + int (*blkread) (struct lis3lv02d *lis3, int reg, int len, u8 *ret); + int (*reg_ctrl) (struct lis3lv02d *lis3, bool state); int *odrs; /* Supported output data rates */ + u8 *regs; /* Regs to store / restore */ + int regs_size; + u8 *reg_cache; + bool regs_stored; u8 odr_mask; /* ODR bit mask */ u8 whoami; /* indicates measurement precision */ s16 (*read_data) (struct lis3lv02d *lis3, int reg); @@ -231,14 +264,18 @@ struct lis3lv02d { struct input_polled_dev *idev; /* input device */ struct platform_device *pdev; /* platform device */ + struct regulator_bulk_data regulators[2]; atomic_t count; /* interrupt count after last read */ - struct axis_conversion ac; /* hw -> logical axis */ + union axis_conversion ac; /* hw -> logical axis */ int mapped_btns[3]; u32 irq; /* IRQ number */ struct fasync_struct *async_queue; /* queue for the misc device */ wait_queue_head_t misc_wait; /* Wait queue for the misc device */ unsigned long misc_opened; /* bit0: whether the device is open */ + int data_ready_count[2]; + atomic_t wake_thread; + unsigned char irq_cfg; struct lis3lv02d_platform_data *pdata; /* for passing board config */ struct mutex mutex; /* Serialize poll and selftest */ diff --git a/drivers/hwmon/lis3lv02d_i2c.c b/drivers/hwmon/lis3lv02d_i2c.c index 8e5933b72d1..9f4bae07f71 100644 --- a/drivers/hwmon/lis3lv02d_i2c.c +++ b/drivers/hwmon/lis3lv02d_i2c.c @@ -29,10 +29,30 @@ #include <linux/init.h> #include <linux/err.h> #include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/delay.h> #include "lis3lv02d.h" #define DRV_NAME "lis3lv02d_i2c" +static const char reg_vdd[] = "Vdd"; +static const char reg_vdd_io[] = "Vdd_IO"; + +static int lis3_reg_ctrl(struct lis3lv02d *lis3, bool state) +{ + int ret; + if (state == LIS3_REG_OFF) { + ret = regulator_bulk_disable(ARRAY_SIZE(lis3->regulators), + lis3->regulators); + } else { + ret = regulator_bulk_enable(ARRAY_SIZE(lis3->regulators), + lis3->regulators); + /* Chip needs time to wakeup. Not mentioned in datasheet */ + usleep_range(10000, 20000); + } + return ret; +} + static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value) { struct i2c_client *c = lis3->bus_priv; @@ -46,24 +66,38 @@ static inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v) return 0; } +static inline s32 lis3_i2c_blockread(struct lis3lv02d *lis3, int reg, int len, + u8 *v) +{ + struct i2c_client *c = lis3->bus_priv; + reg |= (1 << 7); /* 7th bit enables address auto incrementation */ + return i2c_smbus_read_i2c_block_data(c, reg, len, v); +} + static int lis3_i2c_init(struct lis3lv02d *lis3) { u8 reg; int ret; + if (lis3->reg_ctrl) + lis3_reg_ctrl(lis3, LIS3_REG_ON); + + lis3->read(lis3, WHO_AM_I, ®); + if (reg != lis3->whoami) + printk(KERN_ERR "lis3: power on failure\n"); + /* power up the device */ ret = lis3->read(lis3, CTRL_REG1, ®); if (ret < 0) return ret; - reg |= CTRL1_PD0; + reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; return lis3->write(lis3, CTRL_REG1, reg); } /* Default axis mapping but it can be overwritten by platform data */ -static struct axis_conversion lis3lv02d_axis_map = { LIS3_DEV_X, - LIS3_DEV_Y, - LIS3_DEV_Z }; +static union axis_conversion lis3lv02d_axis_map = + { .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } }; static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -72,6 +106,15 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, struct lis3lv02d_platform_data *pdata = client->dev.platform_data; if (pdata) { + /* Regulator control is optional */ + if (pdata->driver_features & LIS3_USE_REGULATOR_CTRL) + lis3_dev.reg_ctrl = lis3_reg_ctrl; + + if ((pdata->driver_features & LIS3_USE_BLOCK_READ) && + (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK))) + lis3_dev.blkread = lis3_i2c_blockread; + if (pdata->axis_x) lis3lv02d_axis_map.x = pdata->axis_x; @@ -88,6 +131,16 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, goto fail; } + if (lis3_dev.reg_ctrl) { + lis3_dev.regulators[0].supply = reg_vdd; + lis3_dev.regulators[1].supply = reg_vdd_io; + ret = regulator_bulk_get(&client->dev, + ARRAY_SIZE(lis3_dev.regulators), + lis3_dev.regulators); + if (ret < 0) + goto fail; + } + lis3_dev.pdata = pdata; lis3_dev.bus_priv = client; lis3_dev.init = lis3_i2c_init; @@ -95,10 +148,24 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, lis3_dev.write = lis3_i2c_write; lis3_dev.irq = client->irq; lis3_dev.ac = lis3lv02d_axis_map; + lis3_dev.pm_dev = &client->dev; i2c_set_clientdata(client, &lis3_dev); + + /* Provide power over the init call */ + if (lis3_dev.reg_ctrl) + lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON); + ret = lis3lv02d_init_device(&lis3_dev); + + if (lis3_dev.reg_ctrl) + lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF); + + if (ret == 0) + return 0; fail: + if (pdata && pdata->release_resources) + pdata->release_resources(); return ret; } @@ -111,14 +178,18 @@ static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client) pdata->release_resources(); lis3lv02d_joystick_disable(); - lis3lv02d_poweroff(lis3); + lis3lv02d_remove_fs(&lis3_dev); - return lis3lv02d_remove_fs(&lis3_dev); + if (lis3_dev.reg_ctrl) + regulator_bulk_free(ARRAY_SIZE(lis3->regulators), + lis3_dev.regulators); + return 0; } #ifdef CONFIG_PM -static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg) +static int lis3lv02d_i2c_suspend(struct device *dev) { + struct i2c_client *client = container_of(dev, struct i2c_client, dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); if (!lis3->pdata || !lis3->pdata->wakeup_flags) @@ -126,18 +197,21 @@ static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int lis3lv02d_i2c_resume(struct i2c_client *client) +static int lis3lv02d_i2c_resume(struct device *dev) { + struct i2c_client *client = container_of(dev, struct i2c_client, dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); - if (!lis3->pdata || !lis3->pdata->wakeup_flags) + /* + * pm_runtime documentation says that devices should always + * be powered on at resume. Pm_runtime turns them off after system + * wide resume is complete. + */ + if (!lis3->pdata || !lis3->pdata->wakeup_flags || + pm_runtime_suspended(dev)) lis3lv02d_poweron(lis3); - return 0; -} -static void lis3lv02d_i2c_shutdown(struct i2c_client *client) -{ - lis3lv02d_i2c_suspend(client, PMSG_SUSPEND); + return 0; } #else #define lis3lv02d_i2c_suspend NULL @@ -145,6 +219,24 @@ static void lis3lv02d_i2c_shutdown(struct i2c_client *client) #define lis3lv02d_i2c_shutdown NULL #endif +static int lis3_i2c_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct lis3lv02d *lis3 = i2c_get_clientdata(client); + + lis3lv02d_poweroff(lis3); + return 0; +} + +static int lis3_i2c_runtime_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct lis3lv02d *lis3 = i2c_get_clientdata(client); + + lis3lv02d_poweron(lis3); + return 0; +} + static const struct i2c_device_id lis3lv02d_id[] = { {"lis3lv02d", 0 }, {} @@ -152,14 +244,20 @@ static const struct i2c_device_id lis3lv02d_id[] = { MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); +static const struct dev_pm_ops lis3_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(lis3lv02d_i2c_suspend, + lis3lv02d_i2c_resume) + SET_RUNTIME_PM_OPS(lis3_i2c_runtime_suspend, + lis3_i2c_runtime_resume, + NULL) +}; + static struct i2c_driver lis3lv02d_i2c_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .pm = &lis3_pm_ops, }, - .suspend = lis3lv02d_i2c_suspend, - .shutdown = lis3lv02d_i2c_shutdown, - .resume = lis3lv02d_i2c_resume, .probe = lis3lv02d_i2c_probe, .remove = __devexit_p(lis3lv02d_i2c_remove), .id_table = lis3lv02d_id, diff --git a/drivers/hwmon/lis3lv02d_spi.c b/drivers/hwmon/lis3lv02d_spi.c index b9be5e3a22b..2549de1de4e 100644 --- a/drivers/hwmon/lis3lv02d_spi.c +++ b/drivers/hwmon/lis3lv02d_spi.c @@ -50,11 +50,12 @@ static int lis3_spi_init(struct lis3lv02d *lis3) if (ret < 0) return ret; - reg |= CTRL1_PD0; + reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; return lis3->write(lis3, CTRL_REG1, reg); } -static struct axis_conversion lis3lv02d_axis_normal = { 1, 2, 3 }; +static union axis_conversion lis3lv02d_axis_normal = + { .as_array = { 1, 2, 3 } }; static int __devinit lis302dl_spi_probe(struct spi_device *spi) { diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c new file mode 100644 index 00000000000..26762617867 --- /dev/null +++ b/drivers/hwmon/ltc4261.c @@ -0,0 +1,315 @@ +/* + * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller + * + * Copyright (C) 2010 Ericsson AB. + * + * Derived from: + * + * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller + * Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu> + * + * Datasheet: http://cds.linear.com/docs/Datasheet/42612fb.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +/* chip registers */ +#define LTC4261_STATUS 0x00 /* readonly */ +#define LTC4261_FAULT 0x01 +#define LTC4261_ALERT 0x02 +#define LTC4261_CONTROL 0x03 +#define LTC4261_SENSE_H 0x04 +#define LTC4261_SENSE_L 0x05 +#define LTC4261_ADIN2_H 0x06 +#define LTC4261_ADIN2_L 0x07 +#define LTC4261_ADIN_H 0x08 +#define LTC4261_ADIN_L 0x09 + +/* + * Fault register bits + */ +#define FAULT_OV (1<<0) +#define FAULT_UV (1<<1) +#define FAULT_OC (1<<2) + +struct ltc4261_data { + struct device *hwmon_dev; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* Registers */ + u8 regs[10]; +}; + +static struct ltc4261_data *ltc4261_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc4261_data *data = i2c_get_clientdata(client); + struct ltc4261_data *ret = data; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ / 4) || !data->valid) { + int i; + + /* Read registers -- 0x00 to 0x09 */ + for (i = 0; i < ARRAY_SIZE(data->regs); i++) { + int val; + + val = i2c_smbus_read_byte_data(client, i); + if (unlikely(val < 0)) { + dev_dbg(dev, + "Failed to read ADC value: error %d", + val); + ret = ERR_PTR(val); + goto abort; + } + data->regs[i] = val; + } + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +/* Return the voltage from the given register in mV or mA */ +static int ltc4261_get_value(struct ltc4261_data *data, u8 reg) +{ + u32 val; + + val = (data->regs[reg] << 2) + (data->regs[reg + 1] >> 6); + + switch (reg) { + case LTC4261_ADIN_H: + case LTC4261_ADIN2_H: + /* 2.5mV resolution. Convert to mV. */ + val = val * 25 / 10; + break; + case LTC4261_SENSE_H: + /* + * 62.5uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = val * 625 / 10; + break; + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + val = 0; + break; + } + + return val; +} + +static ssize_t ltc4261_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ltc4261_data *data = ltc4261_update_device(dev); + int value; + + if (IS_ERR(data)) + return PTR_ERR(data); + + value = ltc4261_get_value(data, attr->index); + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t ltc4261_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct ltc4261_data *data = ltc4261_update_device(dev); + u8 fault; + + if (IS_ERR(data)) + return PTR_ERR(data); + + fault = data->regs[LTC4261_FAULT] & attr->index; + if (fault) /* Clear reported faults in chip register */ + i2c_smbus_write_byte_data(client, LTC4261_FAULT, ~fault); + + return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0); +} + +/* + * These macros are used below in constructing device attribute objects + * for use with sysfs_create_group() to make a sysfs device file + * for each register. + */ + +#define LTC4261_VALUE(name, ltc4261_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4261_show_value, NULL, ltc4261_cmd_idx) + +#define LTC4261_BOOL(name, mask) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4261_show_bool, NULL, (mask)) + +/* + * Input voltages. + */ +LTC4261_VALUE(in1_input, LTC4261_ADIN_H); +LTC4261_VALUE(in2_input, LTC4261_ADIN2_H); + +/* + * Voltage alarms. The chip has only one set of voltage alarm status bits, + * triggered by input voltage alarms. In many designs, those alarms are + * associated with the ADIN2 sensor, due to the proximity of the ADIN2 pin + * to the OV pin. ADIN2 is, however, not available on all chip variants. + * To ensure that the alarm condition is reported to the user, report it + * with both voltage sensors. + */ +LTC4261_BOOL(in1_min_alarm, FAULT_UV); +LTC4261_BOOL(in1_max_alarm, FAULT_OV); +LTC4261_BOOL(in2_min_alarm, FAULT_UV); +LTC4261_BOOL(in2_max_alarm, FAULT_OV); + +/* Currents (via sense resistor) */ +LTC4261_VALUE(curr1_input, LTC4261_SENSE_H); + +/* Overcurrent alarm */ +LTC4261_BOOL(curr1_max_alarm, FAULT_OC); + +static struct attribute *ltc4261_attributes[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, + &sensor_dev_attr_in2_max_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group ltc4261_group = { + .attrs = ltc4261_attributes, +}; + +static int ltc4261_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct ltc4261_data *data; + int ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) { + dev_err(&client->dev, "Failed to read register %d:%02x:%02x\n", + adapter->id, client->addr, LTC4261_STATUS); + return -ENODEV; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto out_kzalloc; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Clear faults */ + i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00); + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, <c4261_group); + if (ret) + goto out_sysfs_create_group; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto out_hwmon_device_register; + } + + return 0; + +out_hwmon_device_register: + sysfs_remove_group(&client->dev.kobj, <c4261_group); +out_sysfs_create_group: + kfree(data); +out_kzalloc: + return ret; +} + +static int ltc4261_remove(struct i2c_client *client) +{ + struct ltc4261_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, <c4261_group); + + kfree(data); + + return 0; +} + +static const struct i2c_device_id ltc4261_id[] = { + {"ltc4261", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ltc4261_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc4261_driver = { + .driver = { + .name = "ltc4261", + }, + .probe = ltc4261_probe, + .remove = ltc4261_remove, + .id_table = ltc4261_id, +}; + +static int __init ltc4261_init(void) +{ + return i2c_add_driver(<c4261_driver); +} + +static void __exit ltc4261_exit(void) +{ + i2c_del_driver(<c4261_driver); +} + +MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>"); +MODULE_DESCRIPTION("LTC4261 driver"); +MODULE_LICENSE("GPL"); + +module_init(ltc4261_init); +module_exit(ltc4261_exit); diff --git a/drivers/hwmon/pkgtemp.c b/drivers/hwmon/pkgtemp.c index f11903936c8..0798210590b 100644 --- a/drivers/hwmon/pkgtemp.c +++ b/drivers/hwmon/pkgtemp.c @@ -21,7 +21,6 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> @@ -35,6 +34,7 @@ #include <linux/cpu.h> #include <asm/msr.h> #include <asm/processor.h> +#include <asm/smp.h> #define DRVNAME "pkgtemp" @@ -339,8 +339,7 @@ exit: return err; } -#ifdef CONFIG_HOTPLUG_CPU -static void pkgtemp_device_remove(unsigned int cpu) +static void __cpuinit pkgtemp_device_remove(unsigned int cpu) { struct pdev_entry *p; unsigned int i; @@ -387,12 +386,10 @@ static int __cpuinit pkgtemp_cpu_callback(struct notifier_block *nfb, static struct notifier_block pkgtemp_cpu_notifier __refdata = { .notifier_call = pkgtemp_cpu_callback, }; -#endif /* !CONFIG_HOTPLUG_CPU */ static int __init pkgtemp_init(void) { int i, err = -ENODEV; - struct pdev_entry *p, *n; /* quick check if we run Intel */ if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL) @@ -402,31 +399,23 @@ static int __init pkgtemp_init(void) if (err) goto exit; - for_each_online_cpu(i) { - err = pkgtemp_device_add(i); - if (err) - goto exit_devices_unreg; - } + for_each_online_cpu(i) + pkgtemp_device_add(i); + +#ifndef CONFIG_HOTPLUG_CPU if (list_empty(&pdev_list)) { err = -ENODEV; goto exit_driver_unreg; } +#endif -#ifdef CONFIG_HOTPLUG_CPU register_hotcpu_notifier(&pkgtemp_cpu_notifier); -#endif return 0; -exit_devices_unreg: - mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); - } - mutex_unlock(&pdev_list_mutex); +#ifndef CONFIG_HOTPLUG_CPU exit_driver_unreg: platform_driver_unregister(&pkgtemp_driver); +#endif exit: return err; } @@ -434,9 +423,8 @@ exit: static void __exit pkgtemp_exit(void) { struct pdev_entry *p, *n; -#ifdef CONFIG_HOTPLUG_CPU + unregister_hotcpu_notifier(&pkgtemp_cpu_notifier); -#endif mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { platform_device_unregister(p->pdev); diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index ffb793af680..ec7fad747ad 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -22,10 +22,8 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/jiffies.h> #include <linux/hwmon.h> #include <linux/sysfs.h> #include <linux/hwmon-sysfs.h> @@ -237,8 +235,7 @@ exit: return err; } -#ifdef CONFIG_HOTPLUG_CPU -static void via_cputemp_device_remove(unsigned int cpu) +static void __cpuinit via_cputemp_device_remove(unsigned int cpu) { struct pdev_entry *p, *n; mutex_lock(&pdev_list_mutex); @@ -272,7 +269,6 @@ static int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb, static struct notifier_block via_cputemp_cpu_notifier __refdata = { .notifier_call = via_cputemp_cpu_callback, }; -#endif /* !CONFIG_HOTPLUG_CPU */ static int __init via_cputemp_init(void) { @@ -313,9 +309,7 @@ static int __init via_cputemp_init(void) goto exit_driver_unreg; } -#ifdef CONFIG_HOTPLUG_CPU register_hotcpu_notifier(&via_cputemp_cpu_notifier); -#endif return 0; exit_devices_unreg: @@ -335,9 +329,8 @@ exit: static void __exit via_cputemp_exit(void) { struct pdev_entry *p, *n; -#ifdef CONFIG_HOTPLUG_CPU + unregister_hotcpu_notifier(&via_cputemp_cpu_notifier); -#endif mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { platform_device_unregister(p->pdev); diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index cb3ccf3ed22..41665d2f9f9 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -74,7 +74,7 @@ static int max_cstate = MWAIT_MAX_NUM_CSTATES - 1; static unsigned int mwait_substates; /* Reliable LAPIC Timer States, bit 1 for C1 etc. */ -static unsigned int lapic_timer_reliable_states; +static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */ static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state); @@ -94,7 +94,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = { .driver_data = (void *) 0x00, .flags = CPUIDLE_FLAG_TIME_VALID, .exit_latency = 3, - .power_usage = 1000, .target_residency = 6, .enter = &intel_idle }, { /* MWAIT C2 */ @@ -103,7 +102,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = { .driver_data = (void *) 0x10, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 20, - .power_usage = 500, .target_residency = 80, .enter = &intel_idle }, { /* MWAIT C3 */ @@ -112,11 +110,46 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = { .driver_data = (void *) 0x20, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 200, - .power_usage = 350, .target_residency = 800, .enter = &intel_idle }, }; +static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = { + { /* MWAIT C0 */ }, + { /* MWAIT C1 */ + .name = "SNB-C1", + .desc = "MWAIT 0x00", + .driver_data = (void *) 0x00, + .flags = CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 1, + .target_residency = 4, + .enter = &intel_idle }, + { /* MWAIT C2 */ + .name = "SNB-C3", + .desc = "MWAIT 0x10", + .driver_data = (void *) 0x10, + .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 80, + .target_residency = 160, + .enter = &intel_idle }, + { /* MWAIT C3 */ + .name = "SNB-C6", + .desc = "MWAIT 0x20", + .driver_data = (void *) 0x20, + .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 104, + .target_residency = 208, + .enter = &intel_idle }, + { /* MWAIT C4 */ + .name = "SNB-C7", + .desc = "MWAIT 0x30", + .driver_data = (void *) 0x30, + .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 109, + .target_residency = 300, + .enter = &intel_idle }, +}; + static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C0 */ }, { /* MWAIT C1 */ @@ -125,7 +158,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = { .driver_data = (void *) 0x00, .flags = CPUIDLE_FLAG_TIME_VALID, .exit_latency = 1, - .power_usage = 1000, .target_residency = 4, .enter = &intel_idle }, { /* MWAIT C2 */ @@ -134,7 +166,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = { .driver_data = (void *) 0x10, .flags = CPUIDLE_FLAG_TIME_VALID, .exit_latency = 20, - .power_usage = 500, .target_residency = 80, .enter = &intel_idle }, { /* MWAIT C3 */ }, @@ -144,7 +175,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = { .driver_data = (void *) 0x30, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 100, - .power_usage = 250, .target_residency = 400, .enter = &intel_idle }, { /* MWAIT C5 */ }, @@ -154,7 +184,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = { .driver_data = (void *) 0x52, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 140, - .power_usage = 150, .target_residency = 560, .enter = &intel_idle }, }; @@ -179,13 +208,10 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state) local_irq_disable(); /* - * If the state flag indicates that the TLB will be flushed or if this - * is the deepest c-state supported, do a voluntary leave mm to avoid - * costly and mostly unnecessary wakeups for flushing the user TLB's - * associated with the active mm. + * leave_mm() to avoid costly and often unnecessary wakeups + * for flushing the user TLB's associated with the active mm. */ - if (state->flags & CPUIDLE_FLAG_TLB_FLUSHED || - (&dev->states[dev->state_count - 1] == state)) + if (state->flags & CPUIDLE_FLAG_TLB_FLUSHED) leave_mm(cpu); if (!(lapic_timer_reliable_states & (1 << (cstate)))) @@ -269,9 +295,14 @@ static int intel_idle_probe(void) case 0x1C: /* 28 - Atom Processor */ case 0x26: /* 38 - Lincroft Atom Processor */ - lapic_timer_reliable_states = (1 << 2) | (1 << 1); /* C2, C1 */ + lapic_timer_reliable_states = (1 << 1); /* C1 */ cpuidle_state_table = atom_cstates; break; + + case 0x2A: /* SNB */ + case 0x2D: /* SNB Xeon */ + cpuidle_state_table = snb_cstates; + break; #ifdef FUTURE_USE case 0x17: /* 23 - Core 2 Duo */ lapic_timer_reliable_states = (1 << 2) | (1 << 1); /* C2, C1 */ diff --git a/drivers/infiniband/core/agent.c b/drivers/infiniband/core/agent.c index ae7c2880e62..91916a8d5de 100644 --- a/drivers/infiniband/core/agent.c +++ b/drivers/infiniband/core/agent.c @@ -59,8 +59,8 @@ __ib_get_agent_port(struct ib_device *device, int port_num) struct ib_agent_port_private *entry; list_for_each_entry(entry, &ib_agent_port_list, port_list) { - if (entry->agent[0]->device == device && - entry->agent[0]->port_num == port_num) + if (entry->agent[1]->device == device && + entry->agent[1]->port_num == port_num) return entry; } return NULL; @@ -155,14 +155,16 @@ int ib_agent_port_open(struct ib_device *device, int port_num) goto error1; } - /* Obtain send only MAD agent for SMI QP */ - port_priv->agent[0] = ib_register_mad_agent(device, port_num, - IB_QPT_SMI, NULL, 0, - &agent_send_handler, - NULL, NULL); - if (IS_ERR(port_priv->agent[0])) { - ret = PTR_ERR(port_priv->agent[0]); - goto error2; + if (rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_INFINIBAND) { + /* Obtain send only MAD agent for SMI QP */ + port_priv->agent[0] = ib_register_mad_agent(device, port_num, + IB_QPT_SMI, NULL, 0, + &agent_send_handler, + NULL, NULL); + if (IS_ERR(port_priv->agent[0])) { + ret = PTR_ERR(port_priv->agent[0]); + goto error2; + } } /* Obtain send only MAD agent for GSI QP */ @@ -182,7 +184,8 @@ int ib_agent_port_open(struct ib_device *device, int port_num) return 0; error3: - ib_unregister_mad_agent(port_priv->agent[0]); + if (port_priv->agent[0]) + ib_unregister_mad_agent(port_priv->agent[0]); error2: kfree(port_priv); error1: @@ -205,7 +208,9 @@ int ib_agent_port_close(struct ib_device *device, int port_num) spin_unlock_irqrestore(&ib_agent_port_list_lock, flags); ib_unregister_mad_agent(port_priv->agent[1]); - ib_unregister_mad_agent(port_priv->agent[0]); + if (port_priv->agent[0]) + ib_unregister_mad_agent(port_priv->agent[0]); + kfree(port_priv); return 0; } diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index b930b8110a6..6884da24fde 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -59,6 +59,7 @@ MODULE_LICENSE("Dual BSD/GPL"); #define CMA_CM_RESPONSE_TIMEOUT 20 #define CMA_MAX_CM_RETRIES 15 #define CMA_CM_MRA_SETTING (IB_CM_MRA_FLAG_DELAY | 24) +#define CMA_IBOE_PACKET_LIFETIME 18 static void cma_add_one(struct ib_device *device); static void cma_remove_one(struct ib_device *device); @@ -157,6 +158,7 @@ struct cma_multicast { struct list_head list; void *context; struct sockaddr_storage addr; + struct kref mcref; }; struct cma_work { @@ -173,6 +175,12 @@ struct cma_ndev_work { struct rdma_cm_event event; }; +struct iboe_mcast_work { + struct work_struct work; + struct rdma_id_private *id; + struct cma_multicast *mc; +}; + union cma_ip_addr { struct in6_addr ip6; struct { @@ -281,6 +289,8 @@ static void cma_attach_to_dev(struct rdma_id_private *id_priv, atomic_inc(&cma_dev->refcount); id_priv->cma_dev = cma_dev; id_priv->id.device = cma_dev->device; + id_priv->id.route.addr.dev_addr.transport = + rdma_node_get_transport(cma_dev->device->node_type); list_add_tail(&id_priv->list, &cma_dev->id_list); } @@ -290,6 +300,14 @@ static inline void cma_deref_dev(struct cma_device *cma_dev) complete(&cma_dev->comp); } +static inline void release_mc(struct kref *kref) +{ + struct cma_multicast *mc = container_of(kref, struct cma_multicast, mcref); + + kfree(mc->multicast.ib); + kfree(mc); +} + static void cma_detach_from_dev(struct rdma_id_private *id_priv) { list_del(&id_priv->list); @@ -323,22 +341,63 @@ static int cma_set_qkey(struct rdma_id_private *id_priv) return ret; } +static int find_gid_port(struct ib_device *device, union ib_gid *gid, u8 port_num) +{ + int i; + int err; + struct ib_port_attr props; + union ib_gid tmp; + + err = ib_query_port(device, port_num, &props); + if (err) + return 1; + + for (i = 0; i < props.gid_tbl_len; ++i) { + err = ib_query_gid(device, port_num, i, &tmp); + if (err) + return 1; + if (!memcmp(&tmp, gid, sizeof tmp)) + return 0; + } + + return -EAGAIN; +} + static int cma_acquire_dev(struct rdma_id_private *id_priv) { struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; struct cma_device *cma_dev; - union ib_gid gid; + union ib_gid gid, iboe_gid; int ret = -ENODEV; + u8 port; + enum rdma_link_layer dev_ll = dev_addr->dev_type == ARPHRD_INFINIBAND ? + IB_LINK_LAYER_INFINIBAND : IB_LINK_LAYER_ETHERNET; - rdma_addr_get_sgid(dev_addr, &gid); + iboe_addr_get_sgid(dev_addr, &iboe_gid); + memcpy(&gid, dev_addr->src_dev_addr + + rdma_addr_gid_offset(dev_addr), sizeof gid); list_for_each_entry(cma_dev, &dev_list, list) { - ret = ib_find_cached_gid(cma_dev->device, &gid, - &id_priv->id.port_num, NULL); - if (!ret) { - cma_attach_to_dev(id_priv, cma_dev); - break; + for (port = 1; port <= cma_dev->device->phys_port_cnt; ++port) { + if (rdma_port_get_link_layer(cma_dev->device, port) == dev_ll) { + if (rdma_node_get_transport(cma_dev->device->node_type) == RDMA_TRANSPORT_IB && + rdma_port_get_link_layer(cma_dev->device, port) == IB_LINK_LAYER_ETHERNET) + ret = find_gid_port(cma_dev->device, &iboe_gid, port); + else + ret = find_gid_port(cma_dev->device, &gid, port); + + if (!ret) { + id_priv->id.port_num = port; + goto out; + } else if (ret == 1) + break; + } } } + +out: + if (!ret) + cma_attach_to_dev(id_priv, cma_dev); + return ret; } @@ -556,10 +615,16 @@ static int cma_ib_init_qp_attr(struct rdma_id_private *id_priv, { struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; int ret; + u16 pkey; + + if (rdma_port_get_link_layer(id_priv->id.device, id_priv->id.port_num) == + IB_LINK_LAYER_INFINIBAND) + pkey = ib_addr_get_pkey(dev_addr); + else + pkey = 0xffff; ret = ib_find_cached_pkey(id_priv->id.device, id_priv->id.port_num, - ib_addr_get_pkey(dev_addr), - &qp_attr->pkey_index); + pkey, &qp_attr->pkey_index); if (ret) return ret; @@ -737,8 +802,8 @@ static inline int cma_user_data_offset(enum rdma_port_space ps) static void cma_cancel_route(struct rdma_id_private *id_priv) { - switch (rdma_node_get_transport(id_priv->id.device->node_type)) { - case RDMA_TRANSPORT_IB: + switch (rdma_port_get_link_layer(id_priv->id.device, id_priv->id.port_num)) { + case IB_LINK_LAYER_INFINIBAND: if (id_priv->query) ib_sa_cancel_query(id_priv->query_id, id_priv->query); break; @@ -816,8 +881,17 @@ static void cma_leave_mc_groups(struct rdma_id_private *id_priv) mc = container_of(id_priv->mc_list.next, struct cma_multicast, list); list_del(&mc->list); - ib_sa_free_multicast(mc->multicast.ib); - kfree(mc); + switch (rdma_port_get_link_layer(id_priv->cma_dev->device, id_priv->id.port_num)) { + case IB_LINK_LAYER_INFINIBAND: + ib_sa_free_multicast(mc->multicast.ib); + kfree(mc); + break; + case IB_LINK_LAYER_ETHERNET: + kref_put(&mc->mcref, release_mc); + break; + default: + break; + } } } @@ -833,7 +907,7 @@ void rdma_destroy_id(struct rdma_cm_id *id) mutex_lock(&lock); if (id_priv->cma_dev) { mutex_unlock(&lock); - switch (rdma_node_get_transport(id->device->node_type)) { + switch (rdma_node_get_transport(id_priv->id.device->node_type)) { case RDMA_TRANSPORT_IB: if (id_priv->cm_id.ib && !IS_ERR(id_priv->cm_id.ib)) ib_destroy_cm_id(id_priv->cm_id.ib); @@ -1708,6 +1782,81 @@ static int cma_resolve_iw_route(struct rdma_id_private *id_priv, int timeout_ms) return 0; } +static int cma_resolve_iboe_route(struct rdma_id_private *id_priv) +{ + struct rdma_route *route = &id_priv->id.route; + struct rdma_addr *addr = &route->addr; + struct cma_work *work; + int ret; + struct sockaddr_in *src_addr = (struct sockaddr_in *)&route->addr.src_addr; + struct sockaddr_in *dst_addr = (struct sockaddr_in *)&route->addr.dst_addr; + struct net_device *ndev = NULL; + u16 vid; + + if (src_addr->sin_family != dst_addr->sin_family) + return -EINVAL; + + work = kzalloc(sizeof *work, GFP_KERNEL); + if (!work) + return -ENOMEM; + + work->id = id_priv; + INIT_WORK(&work->work, cma_work_handler); + + route->path_rec = kzalloc(sizeof *route->path_rec, GFP_KERNEL); + if (!route->path_rec) { + ret = -ENOMEM; + goto err1; + } + + route->num_paths = 1; + + if (addr->dev_addr.bound_dev_if) + ndev = dev_get_by_index(&init_net, addr->dev_addr.bound_dev_if); + if (!ndev) { + ret = -ENODEV; + goto err2; + } + + vid = rdma_vlan_dev_vlan_id(ndev); + + iboe_mac_vlan_to_ll(&route->path_rec->sgid, addr->dev_addr.src_dev_addr, vid); + iboe_mac_vlan_to_ll(&route->path_rec->dgid, addr->dev_addr.dst_dev_addr, vid); + + route->path_rec->hop_limit = 1; + route->path_rec->reversible = 1; + route->path_rec->pkey = cpu_to_be16(0xffff); + route->path_rec->mtu_selector = IB_SA_EQ; + route->path_rec->sl = id_priv->tos >> 5; + + route->path_rec->mtu = iboe_get_mtu(ndev->mtu); + route->path_rec->rate_selector = IB_SA_EQ; + route->path_rec->rate = iboe_get_rate(ndev); + dev_put(ndev); + route->path_rec->packet_life_time_selector = IB_SA_EQ; + route->path_rec->packet_life_time = CMA_IBOE_PACKET_LIFETIME; + if (!route->path_rec->mtu) { + ret = -EINVAL; + goto err2; + } + + work->old_state = CMA_ROUTE_QUERY; + work->new_state = CMA_ROUTE_RESOLVED; + work->event.event = RDMA_CM_EVENT_ROUTE_RESOLVED; + work->event.status = 0; + + queue_work(cma_wq, &work->work); + + return 0; + +err2: + kfree(route->path_rec); + route->path_rec = NULL; +err1: + kfree(work); + return ret; +} + int rdma_resolve_route(struct rdma_cm_id *id, int timeout_ms) { struct rdma_id_private *id_priv; @@ -1720,7 +1869,16 @@ int rdma_resolve_route(struct rdma_cm_id *id, int timeout_ms) atomic_inc(&id_priv->refcount); switch (rdma_node_get_transport(id->device->node_type)) { case RDMA_TRANSPORT_IB: - ret = cma_resolve_ib_route(id_priv, timeout_ms); + switch (rdma_port_get_link_layer(id->device, id->port_num)) { + case IB_LINK_LAYER_INFINIBAND: + ret = cma_resolve_ib_route(id_priv, timeout_ms); + break; + case IB_LINK_LAYER_ETHERNET: + ret = cma_resolve_iboe_route(id_priv); + break; + default: + ret = -ENOSYS; + } break; case RDMA_TRANSPORT_IWARP: ret = cma_resolve_iw_route(id_priv, timeout_ms); @@ -1773,7 +1931,7 @@ port_found: goto out; id_priv->id.route.addr.dev_addr.dev_type = - (rdma_node_get_transport(cma_dev->device->node_type) == RDMA_TRANSPORT_IB) ? + (rdma_port_get_link_layer(cma_dev->device, p) == IB_LINK_LAYER_INFINIBAND) ? ARPHRD_INFINIBAND : ARPHRD_ETHER; rdma_addr_set_sgid(&id_priv->id.route.addr.dev_addr, &gid); @@ -2758,6 +2916,102 @@ static int cma_join_ib_multicast(struct rdma_id_private *id_priv, return 0; } +static void iboe_mcast_work_handler(struct work_struct *work) +{ + struct iboe_mcast_work *mw = container_of(work, struct iboe_mcast_work, work); + struct cma_multicast *mc = mw->mc; + struct ib_sa_multicast *m = mc->multicast.ib; + + mc->multicast.ib->context = mc; + cma_ib_mc_handler(0, m); + kref_put(&mc->mcref, release_mc); + kfree(mw); +} + +static void cma_iboe_set_mgid(struct sockaddr *addr, union ib_gid *mgid) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)addr; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; + + if (cma_any_addr(addr)) { + memset(mgid, 0, sizeof *mgid); + } else if (addr->sa_family == AF_INET6) { + memcpy(mgid, &sin6->sin6_addr, sizeof *mgid); + } else { + mgid->raw[0] = 0xff; + mgid->raw[1] = 0x0e; + mgid->raw[2] = 0; + mgid->raw[3] = 0; + mgid->raw[4] = 0; + mgid->raw[5] = 0; + mgid->raw[6] = 0; + mgid->raw[7] = 0; + mgid->raw[8] = 0; + mgid->raw[9] = 0; + mgid->raw[10] = 0xff; + mgid->raw[11] = 0xff; + *(__be32 *)(&mgid->raw[12]) = sin->sin_addr.s_addr; + } +} + +static int cma_iboe_join_multicast(struct rdma_id_private *id_priv, + struct cma_multicast *mc) +{ + struct iboe_mcast_work *work; + struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; + int err; + struct sockaddr *addr = (struct sockaddr *)&mc->addr; + struct net_device *ndev = NULL; + + if (cma_zero_addr((struct sockaddr *)&mc->addr)) + return -EINVAL; + + work = kzalloc(sizeof *work, GFP_KERNEL); + if (!work) + return -ENOMEM; + + mc->multicast.ib = kzalloc(sizeof(struct ib_sa_multicast), GFP_KERNEL); + if (!mc->multicast.ib) { + err = -ENOMEM; + goto out1; + } + + cma_iboe_set_mgid(addr, &mc->multicast.ib->rec.mgid); + + mc->multicast.ib->rec.pkey = cpu_to_be16(0xffff); + if (id_priv->id.ps == RDMA_PS_UDP) + mc->multicast.ib->rec.qkey = cpu_to_be32(RDMA_UDP_QKEY); + + if (dev_addr->bound_dev_if) + ndev = dev_get_by_index(&init_net, dev_addr->bound_dev_if); + if (!ndev) { + err = -ENODEV; + goto out2; + } + mc->multicast.ib->rec.rate = iboe_get_rate(ndev); + mc->multicast.ib->rec.hop_limit = 1; + mc->multicast.ib->rec.mtu = iboe_get_mtu(ndev->mtu); + dev_put(ndev); + if (!mc->multicast.ib->rec.mtu) { + err = -EINVAL; + goto out2; + } + iboe_addr_get_sgid(dev_addr, &mc->multicast.ib->rec.port_gid); + work->id = id_priv; + work->mc = mc; + INIT_WORK(&work->work, iboe_mcast_work_handler); + kref_get(&mc->mcref); + queue_work(cma_wq, &work->work); + + return 0; + +out2: + kfree(mc->multicast.ib); +out1: + kfree(work); + return err; +} + int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr, void *context) { @@ -2784,7 +3038,17 @@ int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr, switch (rdma_node_get_transport(id->device->node_type)) { case RDMA_TRANSPORT_IB: - ret = cma_join_ib_multicast(id_priv, mc); + switch (rdma_port_get_link_layer(id->device, id->port_num)) { + case IB_LINK_LAYER_INFINIBAND: + ret = cma_join_ib_multicast(id_priv, mc); + break; + case IB_LINK_LAYER_ETHERNET: + kref_init(&mc->mcref); + ret = cma_iboe_join_multicast(id_priv, mc); + break; + default: + ret = -EINVAL; + } break; default: ret = -ENOSYS; @@ -2817,8 +3081,19 @@ void rdma_leave_multicast(struct rdma_cm_id *id, struct sockaddr *addr) ib_detach_mcast(id->qp, &mc->multicast.ib->rec.mgid, mc->multicast.ib->rec.mlid); - ib_sa_free_multicast(mc->multicast.ib); - kfree(mc); + if (rdma_node_get_transport(id_priv->cma_dev->device->node_type) == RDMA_TRANSPORT_IB) { + switch (rdma_port_get_link_layer(id->device, id->port_num)) { + case IB_LINK_LAYER_INFINIBAND: + ib_sa_free_multicast(mc->multicast.ib); + kfree(mc); + break; + case IB_LINK_LAYER_ETHERNET: + kref_put(&mc->mcref, release_mc); + break; + default: + break; + } + } return; } } diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c index bfead5bc25f..2a1e9ae134b 100644 --- a/drivers/infiniband/core/iwcm.c +++ b/drivers/infiniband/core/iwcm.c @@ -506,6 +506,8 @@ int iw_cm_accept(struct iw_cm_id *cm_id, qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); if (!qp) { spin_unlock_irqrestore(&cm_id_priv->lock, flags); + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + wake_up_all(&cm_id_priv->connect_wait); return -EINVAL; } cm_id->device->iwcm->add_ref(qp); @@ -565,6 +567,8 @@ int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); if (!qp) { spin_unlock_irqrestore(&cm_id_priv->lock, flags); + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + wake_up_all(&cm_id_priv->connect_wait); return -EINVAL; } cm_id->device->iwcm->add_ref(qp); diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index ef1304f151d..822cfdcd9f7 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -2598,6 +2598,9 @@ static void cleanup_recv_queue(struct ib_mad_qp_info *qp_info) struct ib_mad_private *recv; struct ib_mad_list_head *mad_list; + if (!qp_info->qp) + return; + while (!list_empty(&qp_info->recv_queue.list)) { mad_list = list_entry(qp_info->recv_queue.list.next, @@ -2639,6 +2642,9 @@ static int ib_mad_port_start(struct ib_mad_port_private *port_priv) for (i = 0; i < IB_MAD_QPS_CORE; i++) { qp = port_priv->qp_info[i].qp; + if (!qp) + continue; + /* * PKey index for QP1 is irrelevant but * one is needed for the Reset to Init transition @@ -2680,6 +2686,9 @@ static int ib_mad_port_start(struct ib_mad_port_private *port_priv) } for (i = 0; i < IB_MAD_QPS_CORE; i++) { + if (!port_priv->qp_info[i].qp) + continue; + ret = ib_mad_post_receive_mads(&port_priv->qp_info[i], NULL); if (ret) { printk(KERN_ERR PFX "Couldn't post receive WRs\n"); @@ -2758,6 +2767,9 @@ error: static void destroy_mad_qp(struct ib_mad_qp_info *qp_info) { + if (!qp_info->qp) + return; + ib_destroy_qp(qp_info->qp); kfree(qp_info->snoop_table); } @@ -2773,6 +2785,7 @@ static int ib_mad_port_open(struct ib_device *device, struct ib_mad_port_private *port_priv; unsigned long flags; char name[sizeof "ib_mad123"]; + int has_smi; /* Create new device info */ port_priv = kzalloc(sizeof *port_priv, GFP_KERNEL); @@ -2788,7 +2801,11 @@ static int ib_mad_port_open(struct ib_device *device, init_mad_qp(port_priv, &port_priv->qp_info[0]); init_mad_qp(port_priv, &port_priv->qp_info[1]); - cq_size = (mad_sendq_size + mad_recvq_size) * 2; + cq_size = mad_sendq_size + mad_recvq_size; + has_smi = rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_INFINIBAND; + if (has_smi) + cq_size *= 2; + port_priv->cq = ib_create_cq(port_priv->device, ib_mad_thread_completion_handler, NULL, port_priv, cq_size, 0); @@ -2812,9 +2829,11 @@ static int ib_mad_port_open(struct ib_device *device, goto error5; } - ret = create_mad_qp(&port_priv->qp_info[0], IB_QPT_SMI); - if (ret) - goto error6; + if (has_smi) { + ret = create_mad_qp(&port_priv->qp_info[0], IB_QPT_SMI); + if (ret) + goto error6; + } ret = create_mad_qp(&port_priv->qp_info[1], IB_QPT_GSI); if (ret) goto error7; diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c index a519801dcfb..68b4162fd9d 100644 --- a/drivers/infiniband/core/multicast.c +++ b/drivers/infiniband/core/multicast.c @@ -774,6 +774,10 @@ static void mcast_event_handler(struct ib_event_handler *handler, int index; dev = container_of(handler, struct mcast_device, event_handler); + if (rdma_port_get_link_layer(dev->device, event->element.port_num) != + IB_LINK_LAYER_INFINIBAND) + return; + index = event->element.port_num - dev->start_port; switch (event->event) { @@ -796,6 +800,7 @@ static void mcast_add_one(struct ib_device *device) struct mcast_device *dev; struct mcast_port *port; int i; + int count = 0; if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB) return; @@ -813,6 +818,9 @@ static void mcast_add_one(struct ib_device *device) } for (i = 0; i <= dev->end_port - dev->start_port; i++) { + if (rdma_port_get_link_layer(device, dev->start_port + i) != + IB_LINK_LAYER_INFINIBAND) + continue; port = &dev->port[i]; port->dev = dev; port->port_num = dev->start_port + i; @@ -820,6 +828,12 @@ static void mcast_add_one(struct ib_device *device) port->table = RB_ROOT; init_completion(&port->comp); atomic_set(&port->refcount, 1); + ++count; + } + + if (!count) { + kfree(dev); + return; } dev->device = device; @@ -843,9 +857,12 @@ static void mcast_remove_one(struct ib_device *device) flush_workqueue(mcast_wq); for (i = 0; i <= dev->end_port - dev->start_port; i++) { - port = &dev->port[i]; - deref_port(port); - wait_for_completion(&port->comp); + if (rdma_port_get_link_layer(device, dev->start_port + i) == + IB_LINK_LAYER_INFINIBAND) { + port = &dev->port[i]; + deref_port(port); + wait_for_completion(&port->comp); + } } kfree(dev); diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 7e1ffd8ccd5..91a660310b7 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -416,6 +416,9 @@ static void ib_sa_event(struct ib_event_handler *handler, struct ib_event *event struct ib_sa_port *port = &sa_dev->port[event->element.port_num - sa_dev->start_port]; + if (rdma_port_get_link_layer(handler->device, port->port_num) != IB_LINK_LAYER_INFINIBAND) + return; + spin_lock_irqsave(&port->ah_lock, flags); if (port->sm_ah) kref_put(&port->sm_ah->ref, free_sm_ah); @@ -493,6 +496,7 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num, { int ret; u16 gid_index; + int force_grh; memset(ah_attr, 0, sizeof *ah_attr); ah_attr->dlid = be16_to_cpu(rec->dlid); @@ -502,7 +506,9 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num, ah_attr->port_num = port_num; ah_attr->static_rate = rec->rate; - if (rec->hop_limit > 1) { + force_grh = rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_ETHERNET; + + if (rec->hop_limit > 1 || force_grh) { ah_attr->ah_flags = IB_AH_GRH; ah_attr->grh.dgid = rec->dgid; @@ -1007,7 +1013,7 @@ static void ib_sa_add_one(struct ib_device *device) e = device->phys_port_cnt; } - sa_dev = kmalloc(sizeof *sa_dev + + sa_dev = kzalloc(sizeof *sa_dev + (e - s + 1) * sizeof (struct ib_sa_port), GFP_KERNEL); if (!sa_dev) @@ -1017,9 +1023,12 @@ static void ib_sa_add_one(struct ib_device *device) sa_dev->end_port = e; for (i = 0; i <= e - s; ++i) { + spin_lock_init(&sa_dev->port[i].ah_lock); + if (rdma_port_get_link_layer(device, i + 1) != IB_LINK_LAYER_INFINIBAND) + continue; + sa_dev->port[i].sm_ah = NULL; sa_dev->port[i].port_num = i + s; - spin_lock_init(&sa_dev->port[i].ah_lock); sa_dev->port[i].agent = ib_register_mad_agent(device, i + s, IB_QPT_GSI, @@ -1045,13 +1054,15 @@ static void ib_sa_add_one(struct ib_device *device) goto err; for (i = 0; i <= e - s; ++i) - update_sm_ah(&sa_dev->port[i].update_task); + if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND) + update_sm_ah(&sa_dev->port[i].update_task); return; err: while (--i >= 0) - ib_unregister_mad_agent(sa_dev->port[i].agent); + if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND) + ib_unregister_mad_agent(sa_dev->port[i].agent); kfree(sa_dev); @@ -1071,9 +1082,12 @@ static void ib_sa_remove_one(struct ib_device *device) flush_scheduled_work(); for (i = 0; i <= sa_dev->end_port - sa_dev->start_port; ++i) { - ib_unregister_mad_agent(sa_dev->port[i].agent); - if (sa_dev->port[i].sm_ah) - kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah); + if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND) { + ib_unregister_mad_agent(sa_dev->port[i].agent); + if (sa_dev->port[i].sm_ah) + kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah); + } + } kfree(sa_dev); diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 3627300e2a1..9ab5df72df7 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -222,6 +222,19 @@ static ssize_t phys_state_show(struct ib_port *p, struct port_attribute *unused, } } +static ssize_t link_layer_show(struct ib_port *p, struct port_attribute *unused, + char *buf) +{ + switch (rdma_port_get_link_layer(p->ibdev, p->port_num)) { + case IB_LINK_LAYER_INFINIBAND: + return sprintf(buf, "%s\n", "InfiniBand"); + case IB_LINK_LAYER_ETHERNET: + return sprintf(buf, "%s\n", "Ethernet"); + default: + return sprintf(buf, "%s\n", "Unknown"); + } +} + static PORT_ATTR_RO(state); static PORT_ATTR_RO(lid); static PORT_ATTR_RO(lid_mask_count); @@ -230,6 +243,7 @@ static PORT_ATTR_RO(sm_sl); static PORT_ATTR_RO(cap_mask); static PORT_ATTR_RO(rate); static PORT_ATTR_RO(phys_state); +static PORT_ATTR_RO(link_layer); static struct attribute *port_default_attrs[] = { &port_attr_state.attr, @@ -240,6 +254,7 @@ static struct attribute *port_default_attrs[] = { &port_attr_cap_mask.attr, &port_attr_rate.attr, &port_attr_phys_state.attr, + &port_attr_link_layer.attr, NULL }; diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index ac7edc24165..ca12acf3837 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -40,6 +40,7 @@ #include <linux/in6.h> #include <linux/miscdevice.h> #include <linux/slab.h> +#include <linux/sysctl.h> #include <rdma/rdma_user_cm.h> #include <rdma/ib_marshall.h> @@ -50,8 +51,24 @@ MODULE_AUTHOR("Sean Hefty"); MODULE_DESCRIPTION("RDMA Userspace Connection Manager Access"); MODULE_LICENSE("Dual BSD/GPL"); -enum { - UCMA_MAX_BACKLOG = 128 +static unsigned int max_backlog = 1024; + +static struct ctl_table_header *ucma_ctl_table_hdr; +static ctl_table ucma_ctl_table[] = { + { + .procname = "max_backlog", + .data = &max_backlog, + .maxlen = sizeof max_backlog, + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { } +}; + +static struct ctl_path ucma_ctl_path[] = { + { .procname = "net" }, + { .procname = "rdma_ucm" }, + { } }; struct ucma_file { @@ -583,6 +600,42 @@ static void ucma_copy_ib_route(struct rdma_ucm_query_route_resp *resp, } } +static void ucma_copy_iboe_route(struct rdma_ucm_query_route_resp *resp, + struct rdma_route *route) +{ + struct rdma_dev_addr *dev_addr; + struct net_device *dev; + u16 vid = 0; + + resp->num_paths = route->num_paths; + switch (route->num_paths) { + case 0: + dev_addr = &route->addr.dev_addr; + dev = dev_get_by_index(&init_net, dev_addr->bound_dev_if); + if (dev) { + vid = rdma_vlan_dev_vlan_id(dev); + dev_put(dev); + } + + iboe_mac_vlan_to_ll((union ib_gid *) &resp->ib_route[0].dgid, + dev_addr->dst_dev_addr, vid); + iboe_addr_get_sgid(dev_addr, + (union ib_gid *) &resp->ib_route[0].sgid); + resp->ib_route[0].pkey = cpu_to_be16(0xffff); + break; + case 2: + ib_copy_path_rec_to_user(&resp->ib_route[1], + &route->path_rec[1]); + /* fall through */ + case 1: + ib_copy_path_rec_to_user(&resp->ib_route[0], + &route->path_rec[0]); + break; + default: + break; + } +} + static ssize_t ucma_query_route(struct ucma_file *file, const char __user *inbuf, int in_len, int out_len) @@ -617,12 +670,17 @@ static ssize_t ucma_query_route(struct ucma_file *file, resp.node_guid = (__force __u64) ctx->cm_id->device->node_guid; resp.port_num = ctx->cm_id->port_num; - switch (rdma_node_get_transport(ctx->cm_id->device->node_type)) { - case RDMA_TRANSPORT_IB: - ucma_copy_ib_route(&resp, &ctx->cm_id->route); - break; - default: - break; + if (rdma_node_get_transport(ctx->cm_id->device->node_type) == RDMA_TRANSPORT_IB) { + switch (rdma_port_get_link_layer(ctx->cm_id->device, ctx->cm_id->port_num)) { + case IB_LINK_LAYER_INFINIBAND: + ucma_copy_ib_route(&resp, &ctx->cm_id->route); + break; + case IB_LINK_LAYER_ETHERNET: + ucma_copy_iboe_route(&resp, &ctx->cm_id->route); + break; + default: + break; + } } out: @@ -686,8 +744,8 @@ static ssize_t ucma_listen(struct ucma_file *file, const char __user *inbuf, if (IS_ERR(ctx)) return PTR_ERR(ctx); - ctx->backlog = cmd.backlog > 0 && cmd.backlog < UCMA_MAX_BACKLOG ? - cmd.backlog : UCMA_MAX_BACKLOG; + ctx->backlog = cmd.backlog > 0 && cmd.backlog < max_backlog ? + cmd.backlog : max_backlog; ret = rdma_listen(ctx->cm_id, ctx->backlog); ucma_put_ctx(ctx); return ret; @@ -1279,16 +1337,26 @@ static int __init ucma_init(void) ret = device_create_file(ucma_misc.this_device, &dev_attr_abi_version); if (ret) { printk(KERN_ERR "rdma_ucm: couldn't create abi_version attr\n"); - goto err; + goto err1; + } + + ucma_ctl_table_hdr = register_sysctl_paths(ucma_ctl_path, ucma_ctl_table); + if (!ucma_ctl_table_hdr) { + printk(KERN_ERR "rdma_ucm: couldn't register sysctl paths\n"); + ret = -ENOMEM; + goto err2; } return 0; -err: +err2: + device_remove_file(ucma_misc.this_device, &dev_attr_abi_version); +err1: misc_deregister(&ucma_misc); return ret; } static void __exit ucma_cleanup(void) { + unregister_sysctl_table(ucma_ctl_table_hdr); device_remove_file(ucma_misc.this_device, &dev_attr_abi_version); misc_deregister(&ucma_misc); idr_destroy(&ctx_idr); diff --git a/drivers/infiniband/core/ud_header.c b/drivers/infiniband/core/ud_header.c index 650b501eb14..bb7e1928082 100644 --- a/drivers/infiniband/core/ud_header.c +++ b/drivers/infiniband/core/ud_header.c @@ -33,6 +33,7 @@ #include <linux/errno.h> #include <linux/string.h> +#include <linux/if_ether.h> #include <rdma/ib_pack.h> @@ -80,6 +81,40 @@ static const struct ib_field lrh_table[] = { .size_bits = 16 } }; +static const struct ib_field eth_table[] = { + { STRUCT_FIELD(eth, dmac_h), + .offset_words = 0, + .offset_bits = 0, + .size_bits = 32 }, + { STRUCT_FIELD(eth, dmac_l), + .offset_words = 1, + .offset_bits = 0, + .size_bits = 16 }, + { STRUCT_FIELD(eth, smac_h), + .offset_words = 1, + .offset_bits = 16, + .size_bits = 16 }, + { STRUCT_FIELD(eth, smac_l), + .offset_words = 2, + .offset_bits = 0, + .size_bits = 32 }, + { STRUCT_FIELD(eth, type), + .offset_words = 3, + .offset_bits = 0, + .size_bits = 16 } +}; + +static const struct ib_field vlan_table[] = { + { STRUCT_FIELD(vlan, tag), + .offset_words = 0, + .offset_bits = 0, + .size_bits = 16 }, + { STRUCT_FIELD(vlan, type), + .offset_words = 0, + .offset_bits = 16, + .size_bits = 16 } +}; + static const struct ib_field grh_table[] = { { STRUCT_FIELD(grh, ip_version), .offset_words = 0, @@ -180,38 +215,43 @@ static const struct ib_field deth_table[] = { /** * ib_ud_header_init - Initialize UD header structure * @payload_bytes:Length of packet payload + * @lrh_present: specify if LRH is present + * @eth_present: specify if Eth header is present + * @vlan_present: packet is tagged vlan * @grh_present:GRH flag (if non-zero, GRH will be included) - * @immediate_present: specify if immediate data should be used + * @immediate_present: specify if immediate data is present * @header:Structure to initialize - * - * ib_ud_header_init() initializes the lrh.link_version, lrh.link_next_header, - * lrh.packet_length, grh.ip_version, grh.payload_length, - * grh.next_header, bth.opcode, bth.pad_count and - * bth.transport_header_version fields of a &struct ib_ud_header given - * the payload length and whether a GRH will be included. */ void ib_ud_header_init(int payload_bytes, + int lrh_present, + int eth_present, + int vlan_present, int grh_present, int immediate_present, struct ib_ud_header *header) { - u16 packet_length; - memset(header, 0, sizeof *header); - header->lrh.link_version = 0; - header->lrh.link_next_header = - grh_present ? IB_LNH_IBA_GLOBAL : IB_LNH_IBA_LOCAL; - packet_length = (IB_LRH_BYTES + - IB_BTH_BYTES + - IB_DETH_BYTES + - payload_bytes + - 4 + /* ICRC */ - 3) / 4; /* round up */ - - header->grh_present = grh_present; + if (lrh_present) { + u16 packet_length; + + header->lrh.link_version = 0; + header->lrh.link_next_header = + grh_present ? IB_LNH_IBA_GLOBAL : IB_LNH_IBA_LOCAL; + packet_length = (IB_LRH_BYTES + + IB_BTH_BYTES + + IB_DETH_BYTES + + (grh_present ? IB_GRH_BYTES : 0) + + payload_bytes + + 4 + /* ICRC */ + 3) / 4; /* round up */ + header->lrh.packet_length = cpu_to_be16(packet_length); + } + + if (vlan_present) + header->eth.type = cpu_to_be16(ETH_P_8021Q); + if (grh_present) { - packet_length += IB_GRH_BYTES / 4; header->grh.ip_version = 6; header->grh.payload_length = cpu_to_be16((IB_BTH_BYTES + @@ -222,19 +262,52 @@ void ib_ud_header_init(int payload_bytes, header->grh.next_header = 0x1b; } - header->lrh.packet_length = cpu_to_be16(packet_length); - - header->immediate_present = immediate_present; if (immediate_present) header->bth.opcode = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE; else header->bth.opcode = IB_OPCODE_UD_SEND_ONLY; header->bth.pad_count = (4 - payload_bytes) & 3; header->bth.transport_header_version = 0; + + header->lrh_present = lrh_present; + header->eth_present = eth_present; + header->vlan_present = vlan_present; + header->grh_present = grh_present; + header->immediate_present = immediate_present; } EXPORT_SYMBOL(ib_ud_header_init); /** + * ib_lrh_header_pack - Pack LRH header struct into wire format + * @lrh:unpacked LRH header struct + * @buf:Buffer to pack into + * + * ib_lrh_header_pack() packs the LRH header structure @lrh into + * wire format in the buffer @buf. + */ +int ib_lrh_header_pack(struct ib_unpacked_lrh *lrh, void *buf) +{ + ib_pack(lrh_table, ARRAY_SIZE(lrh_table), lrh, buf); + return 0; +} +EXPORT_SYMBOL(ib_lrh_header_pack); + +/** + * ib_lrh_header_unpack - Unpack LRH structure from wire format + * @lrh:unpacked LRH header struct + * @buf:Buffer to pack into + * + * ib_lrh_header_unpack() unpacks the LRH header structure from + * wire format (in buf) into @lrh. + */ +int ib_lrh_header_unpack(void *buf, struct ib_unpacked_lrh *lrh) +{ + ib_unpack(lrh_table, ARRAY_SIZE(lrh_table), buf, lrh); + return 0; +} +EXPORT_SYMBOL(ib_lrh_header_unpack); + +/** * ib_ud_header_pack - Pack UD header struct into wire format * @header:UD header struct * @buf:Buffer to pack into @@ -247,10 +320,21 @@ int ib_ud_header_pack(struct ib_ud_header *header, { int len = 0; - ib_pack(lrh_table, ARRAY_SIZE(lrh_table), - &header->lrh, buf); - len += IB_LRH_BYTES; - + if (header->lrh_present) { + ib_pack(lrh_table, ARRAY_SIZE(lrh_table), + &header->lrh, buf + len); + len += IB_LRH_BYTES; + } + if (header->eth_present) { + ib_pack(eth_table, ARRAY_SIZE(eth_table), + &header->eth, buf + len); + len += IB_ETH_BYTES; + } + if (header->vlan_present) { + ib_pack(vlan_table, ARRAY_SIZE(vlan_table), + &header->vlan, buf + len); + len += IB_VLAN_BYTES; + } if (header->grh_present) { ib_pack(grh_table, ARRAY_SIZE(grh_table), &header->grh, buf + len); diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index 5fa85690951..cd1996d0ad0 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c @@ -1022,7 +1022,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num, port->ib_dev = device; port->port_num = port_num; - init_MUTEX(&port->sm_sem); + sema_init(&port->sm_sem, 1); mutex_init(&port->file_mutex); INIT_LIST_HEAD(&port->file_list); diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 6fcfbeb24a2..b342248aec0 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -460,6 +460,8 @@ ssize_t ib_uverbs_query_port(struct ib_uverbs_file *file, resp.active_width = attr.active_width; resp.active_speed = attr.active_speed; resp.phys_state = attr.phys_state; + resp.link_layer = rdma_port_get_link_layer(file->device->ib_dev, + cmd.port_num); if (copy_to_user((void __user *) (unsigned long) cmd.response, &resp, sizeof resp)) diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index e0fa2223871..af7a8b08b2e 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -94,6 +94,22 @@ rdma_node_get_transport(enum rdma_node_type node_type) } EXPORT_SYMBOL(rdma_node_get_transport); +enum rdma_link_layer rdma_port_get_link_layer(struct ib_device *device, u8 port_num) +{ + if (device->get_link_layer) + return device->get_link_layer(device, port_num); + + switch (rdma_node_get_transport(device->node_type)) { + case RDMA_TRANSPORT_IB: + return IB_LINK_LAYER_INFINIBAND; + case RDMA_TRANSPORT_IWARP: + return IB_LINK_LAYER_ETHERNET; + default: + return IB_LINK_LAYER_UNSPECIFIED; + } +} +EXPORT_SYMBOL(rdma_port_get_link_layer); + /* Protection domains */ struct ib_pd *ib_alloc_pd(struct ib_device *device) diff --git a/drivers/infiniband/hw/amso1100/Kbuild b/drivers/infiniband/hw/amso1100/Kbuild index 06964c4af84..950dfabcd89 100644 --- a/drivers/infiniband/hw/amso1100/Kbuild +++ b/drivers/infiniband/hw/amso1100/Kbuild @@ -1,6 +1,4 @@ -ifdef CONFIG_INFINIBAND_AMSO1100_DEBUG -EXTRA_CFLAGS += -DDEBUG -endif +ccflags-$(CONFIG_INFINIBAND_AMSO1100_DEBUG) := -DDEBUG obj-$(CONFIG_INFINIBAND_AMSO1100) += iw_c2.o diff --git a/drivers/infiniband/hw/amso1100/c2_intr.c b/drivers/infiniband/hw/amso1100/c2_intr.c index 3b5095470cb..0ebe4e806b8 100644 --- a/drivers/infiniband/hw/amso1100/c2_intr.c +++ b/drivers/infiniband/hw/amso1100/c2_intr.c @@ -62,8 +62,8 @@ void c2_rnic_interrupt(struct c2_dev *c2dev) static void handle_mq(struct c2_dev *c2dev, u32 mq_index) { if (c2dev->qptr_array[mq_index] == NULL) { - pr_debug(KERN_INFO "handle_mq: stray activity for mq_index=%d\n", - mq_index); + pr_debug("handle_mq: stray activity for mq_index=%d\n", + mq_index); return; } diff --git a/drivers/infiniband/hw/cxgb3/Makefile b/drivers/infiniband/hw/cxgb3/Makefile index 7e7b5a66f04..621619c794e 100644 --- a/drivers/infiniband/hw/cxgb3/Makefile +++ b/drivers/infiniband/hw/cxgb3/Makefile @@ -1,10 +1,8 @@ -EXTRA_CFLAGS += -Idrivers/net/cxgb3 +ccflags-y := -Idrivers/net/cxgb3 obj-$(CONFIG_INFINIBAND_CXGB3) += iw_cxgb3.o iw_cxgb3-y := iwch_cm.o iwch_ev.o iwch_cq.o iwch_qp.o iwch_mem.o \ iwch_provider.o iwch.o cxio_hal.o cxio_resource.o -ifdef CONFIG_INFINIBAND_CXGB3_DEBUG -EXTRA_CFLAGS += -DDEBUG -endif +ccflags-$(CONFIG_INFINIBAND_CXGB3_DEBUG) += -DDEBUG diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.c b/drivers/infiniband/hw/cxgb3/cxio_hal.c index 005b7b52bc1..09dda0b8740 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_hal.c +++ b/drivers/infiniband/hw/cxgb3/cxio_hal.c @@ -160,6 +160,7 @@ int cxio_create_cq(struct cxio_rdev *rdev_p, struct t3_cq *cq, int kernel) struct rdma_cq_setup setup; int size = (1UL << (cq->size_log2)) * sizeof(struct t3_cqe); + size += 1; /* one extra page for storing cq-in-err state */ cq->cqid = cxio_hal_get_cqid(rdev_p->rscp); if (!cq->cqid) return -ENOMEM; diff --git a/drivers/infiniband/hw/cxgb3/cxio_wr.h b/drivers/infiniband/hw/cxgb3/cxio_wr.h index e5ddb63e7d2..4bb997aa39d 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_wr.h +++ b/drivers/infiniband/hw/cxgb3/cxio_wr.h @@ -728,6 +728,22 @@ struct t3_cq { #define CQ_VLD_ENTRY(ptr,size_log2,cqe) (Q_GENBIT(ptr,size_log2) == \ CQE_GENBIT(*cqe)) +struct t3_cq_status_page { + u32 cq_err; +}; + +static inline int cxio_cq_in_error(struct t3_cq *cq) +{ + return ((struct t3_cq_status_page *) + &cq->queue[1 << cq->size_log2])->cq_err; +} + +static inline void cxio_set_cq_in_error(struct t3_cq *cq) +{ + ((struct t3_cq_status_page *) + &cq->queue[1 << cq->size_log2])->cq_err = 1; +} + static inline void cxio_set_wq_in_error(struct t3_wq *wq) { wq->queue->wq_in_err.err |= 1; diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index 13c88871dc3..d02dcc6e596 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -1093,8 +1093,8 @@ static int tx_ack(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) PDBG("%s ep %p credits %u\n", __func__, ep, credits); if (credits == 0) { - PDBG(KERN_ERR "%s 0 credit ack ep %p state %u\n", - __func__, ep, state_read(&ep->com)); + PDBG("%s 0 credit ack ep %p state %u\n", + __func__, ep, state_read(&ep->com)); return CPL_RET_BUF_DONE; } diff --git a/drivers/infiniband/hw/cxgb3/iwch_ev.c b/drivers/infiniband/hw/cxgb3/iwch_ev.c index 6afc89e7572..71e0d845da3 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_ev.c +++ b/drivers/infiniband/hw/cxgb3/iwch_ev.c @@ -76,6 +76,14 @@ static void post_qp_event(struct iwch_dev *rnicp, struct iwch_cq *chp, atomic_inc(&qhp->refcnt); spin_unlock(&rnicp->lock); + if (qhp->attr.state == IWCH_QP_STATE_RTS) { + attrs.next_state = IWCH_QP_STATE_TERMINATE; + iwch_modify_qp(qhp->rhp, qhp, IWCH_QP_ATTR_NEXT_STATE, + &attrs, 1); + if (send_term) + iwch_post_terminate(qhp, rsp_msg); + } + event.event = ib_event; event.device = chp->ibcq.device; if (ib_event == IB_EVENT_CQ_ERR) @@ -86,13 +94,7 @@ static void post_qp_event(struct iwch_dev *rnicp, struct iwch_cq *chp, if (qhp->ibqp.event_handler) (*qhp->ibqp.event_handler)(&event, qhp->ibqp.qp_context); - if (qhp->attr.state == IWCH_QP_STATE_RTS) { - attrs.next_state = IWCH_QP_STATE_TERMINATE; - iwch_modify_qp(qhp->rhp, qhp, IWCH_QP_ATTR_NEXT_STATE, - &attrs, 1); - if (send_term) - iwch_post_terminate(qhp, rsp_msg); - } + (*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context); if (atomic_dec_and_test(&qhp->refcnt)) wake_up(&qhp->wait); @@ -179,7 +181,6 @@ void iwch_ev_dispatch(struct cxio_rdev *rdev_p, struct sk_buff *skb) case TPT_ERR_BOUND: case TPT_ERR_INVALIDATE_SHARED_MR: case TPT_ERR_INVALIDATE_MR_WITH_MW_BOUND: - (*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context); post_qp_event(rnicp, chp, rsp_msg, IB_EVENT_QP_ACCESS_ERR, 1); break; diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index fca0b4b747e..2e2741307af 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -154,6 +154,8 @@ static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int ve struct iwch_create_cq_resp uresp; struct iwch_create_cq_req ureq; struct iwch_ucontext *ucontext = NULL; + static int warned; + size_t resplen; PDBG("%s ib_dev %p entries %d\n", __func__, ibdev, entries); rhp = to_iwch_dev(ibdev); @@ -217,15 +219,26 @@ static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int ve uresp.key = ucontext->key; ucontext->key += PAGE_SIZE; spin_unlock(&ucontext->mmap_lock); - if (ib_copy_to_udata(udata, &uresp, sizeof (uresp))) { + mm->key = uresp.key; + mm->addr = virt_to_phys(chp->cq.queue); + if (udata->outlen < sizeof uresp) { + if (!warned++) + printk(KERN_WARNING MOD "Warning - " + "downlevel libcxgb3 (non-fatal).\n"); + mm->len = PAGE_ALIGN((1UL << uresp.size_log2) * + sizeof(struct t3_cqe)); + resplen = sizeof(struct iwch_create_cq_resp_v0); + } else { + mm->len = PAGE_ALIGN(((1UL << uresp.size_log2) + 1) * + sizeof(struct t3_cqe)); + uresp.memsize = mm->len; + resplen = sizeof uresp; + } + if (ib_copy_to_udata(udata, &uresp, resplen)) { kfree(mm); iwch_destroy_cq(&chp->ibcq); return ERR_PTR(-EFAULT); } - mm->key = uresp.key; - mm->addr = virt_to_phys(chp->cq.queue); - mm->len = PAGE_ALIGN((1UL << uresp.size_log2) * - sizeof (struct t3_cqe)); insert_mmap(ucontext, mm); } PDBG("created cqid 0x%0x chp %p size 0x%0x, dma_addr 0x%0llx\n", @@ -1414,6 +1427,7 @@ int iwch_register_device(struct iwch_dev *dev) dev->ibdev.post_send = iwch_post_send; dev->ibdev.post_recv = iwch_post_receive; dev->ibdev.get_protocol_stats = iwch_get_mib; + dev->ibdev.uverbs_abi_ver = IWCH_UVERBS_ABI_VERSION; dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL); if (!dev->ibdev.iwcm) diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c index c64d27bf2c1..0993137181d 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_qp.c +++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c @@ -802,14 +802,12 @@ int iwch_post_terminate(struct iwch_qp *qhp, struct respQ_msg_t *rsp_msg) /* * Assumes qhp lock is held. */ -static void __flush_qp(struct iwch_qp *qhp, unsigned long *flag) +static void __flush_qp(struct iwch_qp *qhp, struct iwch_cq *rchp, + struct iwch_cq *schp, unsigned long *flag) { - struct iwch_cq *rchp, *schp; int count; int flushed; - rchp = get_chp(qhp->rhp, qhp->attr.rcq); - schp = get_chp(qhp->rhp, qhp->attr.scq); PDBG("%s qhp %p rchp %p schp %p\n", __func__, qhp, rchp, schp); /* take a ref on the qhp since we must release the lock */ @@ -847,10 +845,23 @@ static void __flush_qp(struct iwch_qp *qhp, unsigned long *flag) static void flush_qp(struct iwch_qp *qhp, unsigned long *flag) { - if (qhp->ibqp.uobject) + struct iwch_cq *rchp, *schp; + + rchp = get_chp(qhp->rhp, qhp->attr.rcq); + schp = get_chp(qhp->rhp, qhp->attr.scq); + + if (qhp->ibqp.uobject) { cxio_set_wq_in_error(&qhp->wq); - else - __flush_qp(qhp, flag); + cxio_set_cq_in_error(&rchp->cq); + (*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context); + if (schp != rchp) { + cxio_set_cq_in_error(&schp->cq); + (*schp->ibcq.comp_handler)(&schp->ibcq, + schp->ibcq.cq_context); + } + return; + } + __flush_qp(qhp, rchp, schp, flag); } diff --git a/drivers/infiniband/hw/cxgb3/iwch_user.h b/drivers/infiniband/hw/cxgb3/iwch_user.h index cb7086f558c..a277c31fcaf 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_user.h +++ b/drivers/infiniband/hw/cxgb3/iwch_user.h @@ -45,10 +45,18 @@ struct iwch_create_cq_req { __u64 user_rptr_addr; }; +struct iwch_create_cq_resp_v0 { + __u64 key; + __u32 cqid; + __u32 size_log2; +}; + struct iwch_create_cq_resp { __u64 key; __u32 cqid; __u32 size_log2; + __u32 memsize; + __u32 reserved; }; struct iwch_create_qp_resp { diff --git a/drivers/infiniband/hw/cxgb4/Makefile b/drivers/infiniband/hw/cxgb4/Makefile index e31a499f017..cd20b1342ae 100644 --- a/drivers/infiniband/hw/cxgb4/Makefile +++ b/drivers/infiniband/hw/cxgb4/Makefile @@ -1,4 +1,4 @@ -EXTRA_CFLAGS += -Idrivers/net/cxgb4 +ccflags-y := -Idrivers/net/cxgb4 obj-$(CONFIG_INFINIBAND_CXGB4) += iw_cxgb4.o diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 32d352a88d5..0dc62b1438b 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -117,9 +117,9 @@ static int rcv_win = 256 * 1024; module_param(rcv_win, int, 0644); MODULE_PARM_DESC(rcv_win, "TCP receive window in bytes (default=256KB)"); -static int snd_win = 32 * 1024; +static int snd_win = 128 * 1024; module_param(snd_win, int, 0644); -MODULE_PARM_DESC(snd_win, "TCP send window in bytes (default=32KB)"); +MODULE_PARM_DESC(snd_win, "TCP send window in bytes (default=128KB)"); static struct workqueue_struct *workq; @@ -172,7 +172,7 @@ static int c4iw_l2t_send(struct c4iw_rdev *rdev, struct sk_buff *skb, error = cxgb4_l2t_send(rdev->lldi.ports[0], skb, l2e); if (error < 0) kfree_skb(skb); - return error; + return error < 0 ? error : 0; } int c4iw_ofld_send(struct c4iw_rdev *rdev, struct sk_buff *skb) @@ -187,7 +187,7 @@ int c4iw_ofld_send(struct c4iw_rdev *rdev, struct sk_buff *skb) error = cxgb4_ofld_send(rdev->lldi.ports[0], skb); if (error < 0) kfree_skb(skb); - return error; + return error < 0 ? error : 0; } static void release_tid(struct c4iw_rdev *rdev, u32 hwtid, struct sk_buff *skb) @@ -219,12 +219,11 @@ static void set_emss(struct c4iw_ep *ep, u16 opt) static enum c4iw_ep_state state_read(struct c4iw_ep_common *epc) { - unsigned long flags; enum c4iw_ep_state state; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->mutex); state = epc->state; - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->mutex); return state; } @@ -235,12 +234,10 @@ static void __state_set(struct c4iw_ep_common *epc, enum c4iw_ep_state new) static void state_set(struct c4iw_ep_common *epc, enum c4iw_ep_state new) { - unsigned long flags; - - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->mutex); PDBG("%s - %s -> %s\n", __func__, states[epc->state], states[new]); __state_set(epc, new); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->mutex); return; } @@ -251,8 +248,8 @@ static void *alloc_ep(int size, gfp_t gfp) epc = kzalloc(size, gfp); if (epc) { kref_init(&epc->kref); - spin_lock_init(&epc->lock); - init_waitqueue_head(&epc->waitq); + mutex_init(&epc->mutex); + c4iw_init_wr_wait(&epc->wr_wait); } PDBG("%s alloc ep %p\n", __func__, epc); return epc; @@ -1131,7 +1128,6 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb) { struct c4iw_ep *ep; struct cpl_abort_rpl_rss *rpl = cplhdr(skb); - unsigned long flags; int release = 0; unsigned int tid = GET_TID(rpl); struct tid_info *t = dev->rdev.lldi.tids; @@ -1139,7 +1135,7 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb) ep = lookup_tid(t, tid); PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); BUG_ON(!ep); - spin_lock_irqsave(&ep->com.lock, flags); + mutex_lock(&ep->com.mutex); switch (ep->com.state) { case ABORTING: __state_set(&ep->com, DEAD); @@ -1150,7 +1146,7 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb) __func__, ep, ep->com.state); break; } - spin_unlock_irqrestore(&ep->com.lock, flags); + mutex_unlock(&ep->com.mutex); if (release) release_ep_resources(ep); @@ -1213,9 +1209,9 @@ static int pass_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb) } PDBG("%s ep %p status %d error %d\n", __func__, ep, rpl->status, status2errno(rpl->status)); - ep->com.rpl_err = status2errno(rpl->status); - ep->com.rpl_done = 1; - wake_up(&ep->com.waitq); + ep->com.wr_wait.ret = status2errno(rpl->status); + ep->com.wr_wait.done = 1; + wake_up(&ep->com.wr_wait.wait); return 0; } @@ -1249,9 +1245,9 @@ static int close_listsrv_rpl(struct c4iw_dev *dev, struct sk_buff *skb) struct c4iw_listen_ep *ep = lookup_stid(t, stid); PDBG("%s ep %p\n", __func__, ep); - ep->com.rpl_err = status2errno(rpl->status); - ep->com.rpl_done = 1; - wake_up(&ep->com.waitq); + ep->com.wr_wait.ret = status2errno(rpl->status); + ep->com.wr_wait.done = 1; + wake_up(&ep->com.wr_wait.wait); return 0; } @@ -1478,7 +1474,6 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb) struct cpl_peer_close *hdr = cplhdr(skb); struct c4iw_ep *ep; struct c4iw_qp_attributes attrs; - unsigned long flags; int disconnect = 1; int release = 0; int closing = 0; @@ -1489,7 +1484,7 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb) PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); dst_confirm(ep->dst); - spin_lock_irqsave(&ep->com.lock, flags); + mutex_lock(&ep->com.mutex); switch (ep->com.state) { case MPA_REQ_WAIT: __state_set(&ep->com, CLOSING); @@ -1507,17 +1502,17 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb) * in rdma connection migration (see c4iw_accept_cr()). */ __state_set(&ep->com, CLOSING); - ep->com.rpl_done = 1; - ep->com.rpl_err = -ECONNRESET; + ep->com.wr_wait.done = 1; + ep->com.wr_wait.ret = -ECONNRESET; PDBG("waking up ep %p tid %u\n", ep, ep->hwtid); - wake_up(&ep->com.waitq); + wake_up(&ep->com.wr_wait.wait); break; case MPA_REP_SENT: __state_set(&ep->com, CLOSING); - ep->com.rpl_done = 1; - ep->com.rpl_err = -ECONNRESET; + ep->com.wr_wait.done = 1; + ep->com.wr_wait.ret = -ECONNRESET; PDBG("waking up ep %p tid %u\n", ep, ep->hwtid); - wake_up(&ep->com.waitq); + wake_up(&ep->com.wr_wait.wait); break; case FPDU_MODE: start_ep_timer(ep); @@ -1550,7 +1545,7 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb) default: BUG_ON(1); } - spin_unlock_irqrestore(&ep->com.lock, flags); + mutex_unlock(&ep->com.mutex); if (closing) { attrs.next_state = C4IW_QP_STATE_CLOSING; c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, @@ -1581,7 +1576,6 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) struct c4iw_qp_attributes attrs; int ret; int release = 0; - unsigned long flags; struct tid_info *t = dev->rdev.lldi.tids; unsigned int tid = GET_TID(req); @@ -1591,9 +1585,17 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) ep->hwtid); return 0; } - spin_lock_irqsave(&ep->com.lock, flags); PDBG("%s ep %p tid %u state %u\n", __func__, ep, ep->hwtid, ep->com.state); + + /* + * Wake up any threads in rdma_init() or rdma_fini(). + */ + ep->com.wr_wait.done = 1; + ep->com.wr_wait.ret = -ECONNRESET; + wake_up(&ep->com.wr_wait.wait); + + mutex_lock(&ep->com.mutex); switch (ep->com.state) { case CONNECTING: break; @@ -1605,23 +1607,8 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) connect_reply_upcall(ep, -ECONNRESET); break; case MPA_REP_SENT: - ep->com.rpl_done = 1; - ep->com.rpl_err = -ECONNRESET; - PDBG("waking up ep %p\n", ep); - wake_up(&ep->com.waitq); break; case MPA_REQ_RCVD: - - /* - * We're gonna mark this puppy DEAD, but keep - * the reference on it until the ULP accepts or - * rejects the CR. Also wake up anyone waiting - * in rdma connection migration (see c4iw_accept_cr()). - */ - ep->com.rpl_done = 1; - ep->com.rpl_err = -ECONNRESET; - PDBG("waking up ep %p tid %u\n", ep, ep->hwtid); - wake_up(&ep->com.waitq); break; case MORIBUND: case CLOSING: @@ -1644,7 +1631,7 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) break; case DEAD: PDBG("%s PEER_ABORT IN DEAD STATE!!!!\n", __func__); - spin_unlock_irqrestore(&ep->com.lock, flags); + mutex_unlock(&ep->com.mutex); return 0; default: BUG_ON(1); @@ -1655,7 +1642,7 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) __state_set(&ep->com, DEAD); release = 1; } - spin_unlock_irqrestore(&ep->com.lock, flags); + mutex_unlock(&ep->com.mutex); rpl_skb = get_skb(skb, sizeof(*rpl), GFP_KERNEL); if (!rpl_skb) { @@ -1681,7 +1668,6 @@ static int close_con_rpl(struct c4iw_dev *dev, struct sk_buff *skb) struct c4iw_ep *ep; struct c4iw_qp_attributes attrs; struct cpl_close_con_rpl *rpl = cplhdr(skb); - unsigned long flags; int release = 0; struct tid_info *t = dev->rdev.lldi.tids; unsigned int tid = GET_TID(rpl); @@ -1692,7 +1678,7 @@ static int close_con_rpl(struct c4iw_dev *dev, struct sk_buff *skb) BUG_ON(!ep); /* The cm_id may be null if we failed to connect */ - spin_lock_irqsave(&ep->com.lock, flags); + mutex_lock(&ep->com.mutex); switch (ep->com.state) { case CLOSING: __state_set(&ep->com, MORIBUND); @@ -1717,7 +1703,7 @@ static int close_con_rpl(struct c4iw_dev *dev, struct sk_buff *skb) BUG_ON(1); break; } - spin_unlock_irqrestore(&ep->com.lock, flags); + mutex_unlock(&ep->com.mutex); if (release) release_ep_resources(ep); return 0; @@ -1725,23 +1711,24 @@ static int close_con_rpl(struct c4iw_dev *dev, struct sk_buff *skb) static int terminate(struct c4iw_dev *dev, struct sk_buff *skb) { - struct c4iw_ep *ep; - struct cpl_rdma_terminate *term = cplhdr(skb); + struct cpl_rdma_terminate *rpl = cplhdr(skb); struct tid_info *t = dev->rdev.lldi.tids; - unsigned int tid = GET_TID(term); + unsigned int tid = GET_TID(rpl); + struct c4iw_ep *ep; + struct c4iw_qp_attributes attrs; ep = lookup_tid(t, tid); + BUG_ON(!ep); - if (state_read(&ep->com) != FPDU_MODE) - return 0; + if (ep->com.qp) { + printk(KERN_WARNING MOD "TERM received tid %u qpid %u\n", tid, + ep->com.qp->wq.sq.qid); + attrs.next_state = C4IW_QP_STATE_TERMINATE; + c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); + } else + printk(KERN_WARNING MOD "TERM received tid %u no qp\n", tid); - PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); - skb_pull(skb, sizeof *term); - PDBG("%s saving %d bytes of term msg\n", __func__, skb->len); - skb_copy_from_linear_data(skb, ep->com.qp->attr.terminate_buffer, - skb->len); - ep->com.qp->attr.terminate_msg_len = skb->len; - ep->com.qp->attr.is_terminate_local = 0; return 0; } @@ -1762,8 +1749,8 @@ static int fw4_ack(struct c4iw_dev *dev, struct sk_buff *skb) ep = lookup_tid(t, tid); PDBG("%s ep %p tid %u credits %u\n", __func__, ep, ep->hwtid, credits); if (credits == 0) { - PDBG(KERN_ERR "%s 0 credit ack ep %p tid %u state %u\n", - __func__, ep, ep->hwtid, state_read(&ep->com)); + PDBG("%s 0 credit ack ep %p tid %u state %u\n", + __func__, ep, ep->hwtid, state_read(&ep->com)); return 0; } @@ -2042,6 +2029,7 @@ int c4iw_create_listen(struct iw_cm_id *cm_id, int backlog) } state_set(&ep->com, LISTEN); + c4iw_init_wr_wait(&ep->com.wr_wait); err = cxgb4_create_server(ep->com.dev->rdev.lldi.ports[0], ep->stid, ep->com.local_addr.sin_addr.s_addr, ep->com.local_addr.sin_port, @@ -2050,15 +2038,8 @@ int c4iw_create_listen(struct iw_cm_id *cm_id, int backlog) goto fail3; /* wait for pass_open_rpl */ - wait_event_timeout(ep->com.waitq, ep->com.rpl_done, C4IW_WR_TO); - if (ep->com.rpl_done) - err = ep->com.rpl_err; - else { - printk(KERN_ERR MOD "Device %s not responding!\n", - pci_name(ep->com.dev->rdev.lldi.pdev)); - ep->com.dev->rdev.flags = T4_FATAL_ERROR; - err = -EIO; - } + err = c4iw_wait_for_reply(&ep->com.dev->rdev, &ep->com.wr_wait, 0, 0, + __func__); if (!err) { cm_id->provider_data = ep; goto out; @@ -2082,20 +2063,12 @@ int c4iw_destroy_listen(struct iw_cm_id *cm_id) might_sleep(); state_set(&ep->com, DEAD); - ep->com.rpl_done = 0; - ep->com.rpl_err = 0; + c4iw_init_wr_wait(&ep->com.wr_wait); err = listen_stop(ep); if (err) goto done; - wait_event_timeout(ep->com.waitq, ep->com.rpl_done, C4IW_WR_TO); - if (ep->com.rpl_done) - err = ep->com.rpl_err; - else { - printk(KERN_ERR MOD "Device %s not responding!\n", - pci_name(ep->com.dev->rdev.lldi.pdev)); - ep->com.dev->rdev.flags = T4_FATAL_ERROR; - err = -EIO; - } + err = c4iw_wait_for_reply(&ep->com.dev->rdev, &ep->com.wr_wait, 0, 0, + __func__); cxgb4_free_stid(ep->com.dev->rdev.lldi.tids, ep->stid, PF_INET); done: cm_id->rem_ref(cm_id); @@ -2106,12 +2079,11 @@ done: int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp) { int ret = 0; - unsigned long flags; int close = 0; int fatal = 0; struct c4iw_rdev *rdev; - spin_lock_irqsave(&ep->com.lock, flags); + mutex_lock(&ep->com.mutex); PDBG("%s ep %p state %s, abrupt %d\n", __func__, ep, states[ep->com.state], abrupt); @@ -2158,7 +2130,7 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp) break; } - spin_unlock_irqrestore(&ep->com.lock, flags); + mutex_unlock(&ep->com.mutex); if (close) { if (abrupt) ret = abort_connection(ep, NULL, gfp); @@ -2172,6 +2144,13 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp) return ret; } +static int async_event(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct cpl_fw6_msg *rpl = cplhdr(skb); + c4iw_ev_dispatch(dev, (struct t4_cqe *)&rpl->data[0]); + return 0; +} + /* * These are the real handlers that are called from a * work queue. @@ -2190,7 +2169,8 @@ static c4iw_handler_func work_handlers[NUM_CPL_CMDS] = { [CPL_ABORT_REQ_RSS] = peer_abort, [CPL_CLOSE_CON_RPL] = close_con_rpl, [CPL_RDMA_TERMINATE] = terminate, - [CPL_FW4_ACK] = fw4_ack + [CPL_FW4_ACK] = fw4_ack, + [CPL_FW6_MSG] = async_event }; static void process_timeout(struct c4iw_ep *ep) @@ -2198,7 +2178,7 @@ static void process_timeout(struct c4iw_ep *ep) struct c4iw_qp_attributes attrs; int abort = 1; - spin_lock_irq(&ep->com.lock); + mutex_lock(&ep->com.mutex); PDBG("%s ep %p tid %u state %d\n", __func__, ep, ep->hwtid, ep->com.state); switch (ep->com.state) { @@ -2225,7 +2205,7 @@ static void process_timeout(struct c4iw_ep *ep) WARN_ON(1); abort = 0; } - spin_unlock_irq(&ep->com.lock); + mutex_unlock(&ep->com.mutex); if (abort) abort_connection(ep, NULL, GFP_KERNEL); c4iw_put_ep(&ep->com); @@ -2309,6 +2289,7 @@ static int set_tcb_rpl(struct c4iw_dev *dev, struct sk_buff *skb) printk(KERN_ERR MOD "Unexpected SET_TCB_RPL status %u " "for tid %u\n", rpl->status, GET_TID(rpl)); } + kfree_skb(skb); return 0; } @@ -2323,20 +2304,25 @@ static int fw6_msg(struct c4iw_dev *dev, struct sk_buff *skb) switch (rpl->type) { case 1: ret = (int)((be64_to_cpu(rpl->data[0]) >> 8) & 0xff); - wr_waitp = (__force struct c4iw_wr_wait *)rpl->data[1]; + wr_waitp = (struct c4iw_wr_wait *)(__force unsigned long) rpl->data[1]; PDBG("%s wr_waitp %p ret %u\n", __func__, wr_waitp, ret); if (wr_waitp) { - wr_waitp->ret = ret; + if (ret) + wr_waitp->ret = -ret; + else + wr_waitp->ret = 0; wr_waitp->done = 1; wake_up(&wr_waitp->wait); } + kfree_skb(skb); break; case 2: - c4iw_ev_dispatch(dev, (struct t4_cqe *)&rpl->data[0]); + sched(dev, skb); break; default: printk(KERN_ERR MOD "%s unexpected fw6 msg type %u\n", __func__, rpl->type); + kfree_skb(skb); break; } return 0; diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c index b3daf39eed4..8d8f8add6fc 100644 --- a/drivers/infiniband/hw/cxgb4/cq.c +++ b/drivers/infiniband/hw/cxgb4/cq.c @@ -55,7 +55,7 @@ static int destroy_cq(struct c4iw_rdev *rdev, struct t4_cq *cq, V_FW_RI_RES_WR_NRES(1) | FW_WR_COMPL(1)); res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16)); - res_wr->cookie = (u64)&wr_wait; + res_wr->cookie = (unsigned long) &wr_wait; res = res_wr->res; res->u.cq.restype = FW_RI_RES_TYPE_CQ; res->u.cq.op = FW_RI_RES_OP_RESET; @@ -64,14 +64,7 @@ static int destroy_cq(struct c4iw_rdev *rdev, struct t4_cq *cq, c4iw_init_wr_wait(&wr_wait); ret = c4iw_ofld_send(rdev, skb); if (!ret) { - wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); - if (!wr_wait.done) { - printk(KERN_ERR MOD "Device %s not responding!\n", - pci_name(rdev->lldi.pdev)); - rdev->flags = T4_FATAL_ERROR; - ret = -EIO; - } else - ret = wr_wait.ret; + ret = c4iw_wait_for_reply(rdev, &wr_wait, 0, 0, __func__); } kfree(cq->sw_queue); @@ -132,7 +125,7 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq, V_FW_RI_RES_WR_NRES(1) | FW_WR_COMPL(1)); res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16)); - res_wr->cookie = (u64)&wr_wait; + res_wr->cookie = (unsigned long) &wr_wait; res = res_wr->res; res->u.cq.restype = FW_RI_RES_TYPE_CQ; res->u.cq.op = FW_RI_RES_OP_WRITE; @@ -157,14 +150,7 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq, if (ret) goto err4; PDBG("%s wait_event wr_wait %p\n", __func__, &wr_wait); - wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); - if (!wr_wait.done) { - printk(KERN_ERR MOD "Device %s not responding!\n", - pci_name(rdev->lldi.pdev)); - rdev->flags = T4_FATAL_ERROR; - ret = -EIO; - } else - ret = wr_wait.ret; + ret = c4iw_wait_for_reply(rdev, &wr_wait, 0, 0, __func__); if (ret) goto err4; @@ -476,6 +462,11 @@ static int poll_cq(struct t4_wq *wq, struct t4_cq *cq, struct t4_cqe *cqe, goto proc_cqe; } + if (CQE_OPCODE(hw_cqe) == FW_RI_TERMINATE) { + ret = -EAGAIN; + goto skip_cqe; + } + /* * RECV completion. */ @@ -696,6 +687,7 @@ static int c4iw_poll_cq_one(struct c4iw_cq *chp, struct ib_wc *wc) case T4_ERR_MSN_RANGE: case T4_ERR_IRD_OVERFLOW: case T4_ERR_OPCODE: + case T4_ERR_INTERNAL_ERR: wc->status = IB_WC_FATAL_ERR; break; case T4_ERR_SWFLUSH: diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index 9bbf491d5d9..54fbc1118ab 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -49,29 +49,33 @@ static DEFINE_MUTEX(dev_mutex); static struct dentry *c4iw_debugfs_root; -struct debugfs_qp_data { +struct c4iw_debugfs_data { struct c4iw_dev *devp; char *buf; int bufsize; int pos; }; -static int count_qps(int id, void *p, void *data) +static int count_idrs(int id, void *p, void *data) { - struct c4iw_qp *qp = p; int *countp = data; - if (id != qp->wq.sq.qid) - return 0; - *countp = *countp + 1; return 0; } -static int dump_qps(int id, void *p, void *data) +static ssize_t debugfs_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + struct c4iw_debugfs_data *d = file->private_data; + + return simple_read_from_buffer(buf, count, ppos, d->buf, d->pos); +} + +static int dump_qp(int id, void *p, void *data) { struct c4iw_qp *qp = p; - struct debugfs_qp_data *qpd = data; + struct c4iw_debugfs_data *qpd = data; int space; int cc; @@ -101,7 +105,7 @@ static int dump_qps(int id, void *p, void *data) static int qp_release(struct inode *inode, struct file *file) { - struct debugfs_qp_data *qpd = file->private_data; + struct c4iw_debugfs_data *qpd = file->private_data; if (!qpd) { printk(KERN_INFO "%s null qpd?\n", __func__); return 0; @@ -113,7 +117,7 @@ static int qp_release(struct inode *inode, struct file *file) static int qp_open(struct inode *inode, struct file *file) { - struct debugfs_qp_data *qpd; + struct c4iw_debugfs_data *qpd; int ret = 0; int count = 1; @@ -126,7 +130,7 @@ static int qp_open(struct inode *inode, struct file *file) qpd->pos = 0; spin_lock_irq(&qpd->devp->lock); - idr_for_each(&qpd->devp->qpidr, count_qps, &count); + idr_for_each(&qpd->devp->qpidr, count_idrs, &count); spin_unlock_irq(&qpd->devp->lock); qpd->bufsize = count * 128; @@ -137,7 +141,7 @@ static int qp_open(struct inode *inode, struct file *file) } spin_lock_irq(&qpd->devp->lock); - idr_for_each(&qpd->devp->qpidr, dump_qps, qpd); + idr_for_each(&qpd->devp->qpidr, dump_qp, qpd); spin_unlock_irq(&qpd->devp->lock); qpd->buf[qpd->pos++] = 0; @@ -149,43 +153,86 @@ out: return ret; } -static ssize_t qp_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) +static const struct file_operations qp_debugfs_fops = { + .owner = THIS_MODULE, + .open = qp_open, + .release = qp_release, + .read = debugfs_read, + .llseek = default_llseek, +}; + +static int dump_stag(int id, void *p, void *data) { - struct debugfs_qp_data *qpd = file->private_data; - loff_t pos = *ppos; - loff_t avail = qpd->pos; + struct c4iw_debugfs_data *stagd = data; + int space; + int cc; - if (pos < 0) - return -EINVAL; - if (pos >= avail) + space = stagd->bufsize - stagd->pos - 1; + if (space == 0) + return 1; + + cc = snprintf(stagd->buf + stagd->pos, space, "0x%x\n", id<<8); + if (cc < space) + stagd->pos += cc; + return 0; +} + +static int stag_release(struct inode *inode, struct file *file) +{ + struct c4iw_debugfs_data *stagd = file->private_data; + if (!stagd) { + printk(KERN_INFO "%s null stagd?\n", __func__); return 0; - if (count > avail - pos) - count = avail - pos; + } + kfree(stagd->buf); + kfree(stagd); + return 0; +} - while (count) { - size_t len = 0; +static int stag_open(struct inode *inode, struct file *file) +{ + struct c4iw_debugfs_data *stagd; + int ret = 0; + int count = 1; - len = min((int)count, (int)qpd->pos - (int)pos); - if (copy_to_user(buf, qpd->buf + pos, len)) - return -EFAULT; - if (len == 0) - return -EINVAL; + stagd = kmalloc(sizeof *stagd, GFP_KERNEL); + if (!stagd) { + ret = -ENOMEM; + goto out; + } + stagd->devp = inode->i_private; + stagd->pos = 0; + + spin_lock_irq(&stagd->devp->lock); + idr_for_each(&stagd->devp->mmidr, count_idrs, &count); + spin_unlock_irq(&stagd->devp->lock); - buf += len; - pos += len; - count -= len; + stagd->bufsize = count * sizeof("0x12345678\n"); + stagd->buf = kmalloc(stagd->bufsize, GFP_KERNEL); + if (!stagd->buf) { + ret = -ENOMEM; + goto err1; } - count = pos - *ppos; - *ppos = pos; - return count; + + spin_lock_irq(&stagd->devp->lock); + idr_for_each(&stagd->devp->mmidr, dump_stag, stagd); + spin_unlock_irq(&stagd->devp->lock); + + stagd->buf[stagd->pos++] = 0; + file->private_data = stagd; + goto out; +err1: + kfree(stagd); +out: + return ret; } -static const struct file_operations qp_debugfs_fops = { +static const struct file_operations stag_debugfs_fops = { .owner = THIS_MODULE, - .open = qp_open, - .release = qp_release, - .read = qp_read, + .open = stag_open, + .release = stag_release, + .read = debugfs_read, + .llseek = default_llseek, }; static int setup_debugfs(struct c4iw_dev *devp) @@ -199,6 +246,11 @@ static int setup_debugfs(struct c4iw_dev *devp) (void *)devp, &qp_debugfs_fops); if (de && de->d_inode) de->d_inode->i_size = 4096; + + de = debugfs_create_file("stags", S_IWUSR, devp->debugfs_root, + (void *)devp, &stag_debugfs_fops); + if (de && de->d_inode) + de->d_inode->i_size = 4096; return 0; } @@ -290,7 +342,14 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev) printk(KERN_ERR MOD "error %d initializing rqt pool\n", err); goto err3; } + err = c4iw_ocqp_pool_create(rdev); + if (err) { + printk(KERN_ERR MOD "error %d initializing ocqp pool\n", err); + goto err4; + } return 0; +err4: + c4iw_rqtpool_destroy(rdev); err3: c4iw_pblpool_destroy(rdev); err2: @@ -317,6 +376,7 @@ static void c4iw_remove(struct c4iw_dev *dev) idr_destroy(&dev->cqidr); idr_destroy(&dev->qpidr); idr_destroy(&dev->mmidr); + iounmap(dev->rdev.oc_mw_kva); ib_dealloc_device(&dev->ibdev); } @@ -332,6 +392,17 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop) } devp->rdev.lldi = *infop; + devp->rdev.oc_mw_pa = pci_resource_start(devp->rdev.lldi.pdev, 2) + + (pci_resource_len(devp->rdev.lldi.pdev, 2) - + roundup_pow_of_two(devp->rdev.lldi.vr->ocq.size)); + devp->rdev.oc_mw_kva = ioremap_wc(devp->rdev.oc_mw_pa, + devp->rdev.lldi.vr->ocq.size); + + printk(KERN_INFO MOD "ocq memory: " + "hw_start 0x%x size %u mw_pa 0x%lx mw_kva %p\n", + devp->rdev.lldi.vr->ocq.start, devp->rdev.lldi.vr->ocq.size, + devp->rdev.oc_mw_pa, devp->rdev.oc_mw_kva); + mutex_lock(&dev_mutex); ret = c4iw_rdev_open(&devp->rdev); @@ -383,46 +454,6 @@ out: return dev; } -static struct sk_buff *t4_pktgl_to_skb(const struct pkt_gl *gl, - unsigned int skb_len, - unsigned int pull_len) -{ - struct sk_buff *skb; - struct skb_shared_info *ssi; - - if (gl->tot_len <= 512) { - skb = alloc_skb(gl->tot_len, GFP_ATOMIC); - if (unlikely(!skb)) - goto out; - __skb_put(skb, gl->tot_len); - skb_copy_to_linear_data(skb, gl->va, gl->tot_len); - } else { - skb = alloc_skb(skb_len, GFP_ATOMIC); - if (unlikely(!skb)) - goto out; - __skb_put(skb, pull_len); - skb_copy_to_linear_data(skb, gl->va, pull_len); - - ssi = skb_shinfo(skb); - ssi->frags[0].page = gl->frags[0].page; - ssi->frags[0].page_offset = gl->frags[0].page_offset + pull_len; - ssi->frags[0].size = gl->frags[0].size - pull_len; - if (gl->nfrags > 1) - memcpy(&ssi->frags[1], &gl->frags[1], - (gl->nfrags - 1) * sizeof(skb_frag_t)); - ssi->nr_frags = gl->nfrags; - - skb->len = gl->tot_len; - skb->data_len = skb->len - pull_len; - skb->truesize += skb->data_len; - - /* Get a reference for the last page, we don't own it */ - get_page(gl->frags[gl->nfrags - 1].page); - } -out: - return skb; -} - static int c4iw_uld_rx_handler(void *handle, const __be64 *rsp, const struct pkt_gl *gl) { @@ -447,7 +478,7 @@ static int c4iw_uld_rx_handler(void *handle, const __be64 *rsp, c4iw_ev_handler(dev, qid); return 0; } else { - skb = t4_pktgl_to_skb(gl, 128, 128); + skb = cxgb4_pktgl_to_skb(gl, 128, 128); if (unlikely(!skb)) goto nomem; } diff --git a/drivers/infiniband/hw/cxgb4/ev.c b/drivers/infiniband/hw/cxgb4/ev.c index 491e76a0327..c13041a0aeb 100644 --- a/drivers/infiniband/hw/cxgb4/ev.c +++ b/drivers/infiniband/hw/cxgb4/ev.c @@ -60,7 +60,7 @@ static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp, if (qhp->attr.state == C4IW_QP_STATE_RTS) { attrs.next_state = C4IW_QP_STATE_TERMINATE; c4iw_modify_qp(qhp->rhp, qhp, C4IW_QP_ATTR_NEXT_STATE, - &attrs, 1); + &attrs, 0); } event.event = ib_event; diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h index ed459b8f800..16032cdb433 100644 --- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h +++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h @@ -46,6 +46,7 @@ #include <linux/timer.h> #include <linux/io.h> #include <linux/kfifo.h> +#include <linux/mutex.h> #include <asm/byteorder.h> @@ -79,21 +80,6 @@ static inline void *cplhdr(struct sk_buff *skb) return skb->data; } -#define C4IW_WR_TO (10*HZ) - -struct c4iw_wr_wait { - wait_queue_head_t wait; - int done; - int ret; -}; - -static inline void c4iw_init_wr_wait(struct c4iw_wr_wait *wr_waitp) -{ - wr_waitp->ret = 0; - wr_waitp->done = 0; - init_waitqueue_head(&wr_waitp->wait); -} - struct c4iw_resource { struct kfifo tpt_fifo; spinlock_t tpt_fifo_lock; @@ -127,8 +113,11 @@ struct c4iw_rdev { struct c4iw_dev_ucontext uctx; struct gen_pool *pbl_pool; struct gen_pool *rqt_pool; + struct gen_pool *ocqp_pool; u32 flags; struct cxgb4_lld_info lldi; + unsigned long oc_mw_pa; + void __iomem *oc_mw_kva; }; static inline int c4iw_fatal_error(struct c4iw_rdev *rdev) @@ -141,6 +130,44 @@ static inline int c4iw_num_stags(struct c4iw_rdev *rdev) return min((int)T4_MAX_NUM_STAG, (int)(rdev->lldi.vr->stag.size >> 5)); } +#define C4IW_WR_TO (10*HZ) + +struct c4iw_wr_wait { + wait_queue_head_t wait; + int done; + int ret; +}; + +static inline void c4iw_init_wr_wait(struct c4iw_wr_wait *wr_waitp) +{ + wr_waitp->ret = 0; + wr_waitp->done = 0; + init_waitqueue_head(&wr_waitp->wait); +} + +static inline int c4iw_wait_for_reply(struct c4iw_rdev *rdev, + struct c4iw_wr_wait *wr_waitp, + u32 hwtid, u32 qpid, + const char *func) +{ + unsigned to = C4IW_WR_TO; + do { + + wait_event_timeout(wr_waitp->wait, wr_waitp->done, to); + if (!wr_waitp->done) { + printk(KERN_ERR MOD "%s - Device %s not responding - " + "tid %u qpid %u\n", func, + pci_name(rdev->lldi.pdev), hwtid, qpid); + to = to << 2; + } + } while (!wr_waitp->done); + if (wr_waitp->ret) + printk(KERN_WARNING MOD "%s: FW reply %d tid %u qpid %u\n", + pci_name(rdev->lldi.pdev), wr_waitp->ret, hwtid, qpid); + return wr_waitp->ret; +} + + struct c4iw_dev { struct ib_device ibdev; struct c4iw_rdev rdev; @@ -327,6 +354,7 @@ struct c4iw_qp { struct c4iw_qp_attributes attr; struct t4_wq wq; spinlock_t lock; + struct mutex mutex; atomic_t refcnt; wait_queue_head_t wait; struct timer_list timer; @@ -579,12 +607,10 @@ struct c4iw_ep_common { struct c4iw_dev *dev; enum c4iw_ep_state state; struct kref kref; - spinlock_t lock; + struct mutex mutex; struct sockaddr_in local_addr; struct sockaddr_in remote_addr; - wait_queue_head_t waitq; - int rpl_done; - int rpl_err; + struct c4iw_wr_wait wr_wait; unsigned long flags; }; @@ -654,8 +680,10 @@ int c4iw_init_resource(struct c4iw_rdev *rdev, u32 nr_tpt, u32 nr_pdid); int c4iw_init_ctrl_qp(struct c4iw_rdev *rdev); int c4iw_pblpool_create(struct c4iw_rdev *rdev); int c4iw_rqtpool_create(struct c4iw_rdev *rdev); +int c4iw_ocqp_pool_create(struct c4iw_rdev *rdev); void c4iw_pblpool_destroy(struct c4iw_rdev *rdev); void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev); +void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev); void c4iw_destroy_resource(struct c4iw_resource *rscp); int c4iw_destroy_ctrl_qp(struct c4iw_rdev *rdev); int c4iw_register_device(struct c4iw_dev *dev); @@ -721,6 +749,8 @@ u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size); void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size); u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size); void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size); +u32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size); +void c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size); int c4iw_ofld_send(struct c4iw_rdev *rdev, struct sk_buff *skb); void c4iw_flush_hw_cq(struct t4_cq *cq); void c4iw_count_rcqes(struct t4_cq *cq, struct t4_wq *wq, int *count); diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index 269373a62f2..273ffe49525 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -71,7 +71,7 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len, if (i == (num_wqe-1)) { req->wr.wr_hi = cpu_to_be32(FW_WR_OP(FW_ULPTX_WR) | FW_WR_COMPL(1)); - req->wr.wr_lo = (__force __be64)&wr_wait; + req->wr.wr_lo = (__force __be64)(unsigned long) &wr_wait; } else req->wr.wr_hi = cpu_to_be32(FW_WR_OP(FW_ULPTX_WR)); req->wr.wr_mid = cpu_to_be32( @@ -103,14 +103,7 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len, len -= C4IW_MAX_INLINE_SIZE; } - wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); - if (!wr_wait.done) { - printk(KERN_ERR MOD "Device %s not responding!\n", - pci_name(rdev->lldi.pdev)); - rdev->flags = T4_FATAL_ERROR; - ret = -EIO; - } else - ret = wr_wait.ret; + ret = c4iw_wait_for_reply(rdev, &wr_wait, 0, 0, __func__); return ret; } diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c index 8f645c83a12..f66dd8bf512 100644 --- a/drivers/infiniband/hw/cxgb4/provider.c +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -54,9 +54,9 @@ #include "iw_cxgb4.h" -static int fastreg_support; +static int fastreg_support = 1; module_param(fastreg_support, int, 0644); -MODULE_PARM_DESC(fastreg_support, "Advertise fastreg support (default=0)"); +MODULE_PARM_DESC(fastreg_support, "Advertise fastreg support (default=1)"); static int c4iw_modify_port(struct ib_device *ibdev, u8 port, int port_modify_mask, @@ -149,19 +149,28 @@ static int c4iw_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) addr = mm->addr; kfree(mm); - if ((addr >= pci_resource_start(rdev->lldi.pdev, 2)) && - (addr < (pci_resource_start(rdev->lldi.pdev, 2) + - pci_resource_len(rdev->lldi.pdev, 2)))) { + if ((addr >= pci_resource_start(rdev->lldi.pdev, 0)) && + (addr < (pci_resource_start(rdev->lldi.pdev, 0) + + pci_resource_len(rdev->lldi.pdev, 0)))) { /* - * Map T4 DB register. + * MA_SYNC register... */ - if (vma->vm_flags & VM_READ) - return -EPERM; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND; - vma->vm_flags &= ~VM_MAYREAD; + ret = io_remap_pfn_range(vma, vma->vm_start, + addr >> PAGE_SHIFT, + len, vma->vm_page_prot); + } else if ((addr >= pci_resource_start(rdev->lldi.pdev, 2)) && + (addr < (pci_resource_start(rdev->lldi.pdev, 2) + + pci_resource_len(rdev->lldi.pdev, 2)))) { + + /* + * Map user DB or OCQP memory... + */ + if (addr >= rdev->oc_mw_pa) + vma->vm_page_prot = t4_pgprot_wc(vma->vm_page_prot); + else + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ret = io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, len, vma->vm_page_prot); @@ -382,7 +391,17 @@ static ssize_t show_board(struct device *dev, struct device_attribute *attr, static int c4iw_get_mib(struct ib_device *ibdev, union rdma_protocol_stats *stats) { - return -ENOSYS; + struct tp_tcp_stats v4, v6; + struct c4iw_dev *c4iw_dev = to_c4iw_dev(ibdev); + + cxgb4_get_tcp_stats(c4iw_dev->rdev.lldi.pdev, &v4, &v6); + memset(stats, 0, sizeof *stats); + stats->iw.tcpInSegs = v4.tcpInSegs + v6.tcpInSegs; + stats->iw.tcpOutSegs = v4.tcpOutSegs + v6.tcpOutSegs; + stats->iw.tcpRetransSegs = v4.tcpRetransSegs + v6.tcpRetransSegs; + stats->iw.tcpOutRsts = v4.tcpOutRsts + v6.tcpOutSegs; + + return 0; } static DEVICE_ATTR(hw_rev, S_IRUGO, show_rev, NULL); @@ -472,6 +491,7 @@ int c4iw_register_device(struct c4iw_dev *dev) dev->ibdev.post_send = c4iw_post_send; dev->ibdev.post_recv = c4iw_post_receive; dev->ibdev.get_protocol_stats = c4iw_get_mib; + dev->ibdev.uverbs_abi_ver = C4IW_UVERBS_ABI_VERSION; dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL); if (!dev->ibdev.iwcm) diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index 93f6e5bf0ec..057cb2505ea 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -31,6 +31,63 @@ */ #include "iw_cxgb4.h" +static int ocqp_support; +module_param(ocqp_support, int, 0644); +MODULE_PARM_DESC(ocqp_support, "Support on-chip SQs (default=0)"); + +static void set_state(struct c4iw_qp *qhp, enum c4iw_qp_state state) +{ + unsigned long flag; + spin_lock_irqsave(&qhp->lock, flag); + qhp->attr.state = state; + spin_unlock_irqrestore(&qhp->lock, flag); +} + +static void dealloc_oc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq) +{ + c4iw_ocqp_pool_free(rdev, sq->dma_addr, sq->memsize); +} + +static void dealloc_host_sq(struct c4iw_rdev *rdev, struct t4_sq *sq) +{ + dma_free_coherent(&(rdev->lldi.pdev->dev), sq->memsize, sq->queue, + pci_unmap_addr(sq, mapping)); +} + +static void dealloc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq) +{ + if (t4_sq_onchip(sq)) + dealloc_oc_sq(rdev, sq); + else + dealloc_host_sq(rdev, sq); +} + +static int alloc_oc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq) +{ + if (!ocqp_support || !t4_ocqp_supported()) + return -ENOSYS; + sq->dma_addr = c4iw_ocqp_pool_alloc(rdev, sq->memsize); + if (!sq->dma_addr) + return -ENOMEM; + sq->phys_addr = rdev->oc_mw_pa + sq->dma_addr - + rdev->lldi.vr->ocq.start; + sq->queue = (__force union t4_wr *)(rdev->oc_mw_kva + sq->dma_addr - + rdev->lldi.vr->ocq.start); + sq->flags |= T4_SQ_ONCHIP; + return 0; +} + +static int alloc_host_sq(struct c4iw_rdev *rdev, struct t4_sq *sq) +{ + sq->queue = dma_alloc_coherent(&(rdev->lldi.pdev->dev), sq->memsize, + &(sq->dma_addr), GFP_KERNEL); + if (!sq->queue) + return -ENOMEM; + sq->phys_addr = virt_to_phys(sq->queue); + pci_unmap_addr_set(sq, mapping, sq->dma_addr); + return 0; +} + static int destroy_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, struct c4iw_dev_ucontext *uctx) { @@ -41,9 +98,7 @@ static int destroy_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, dma_free_coherent(&(rdev->lldi.pdev->dev), wq->rq.memsize, wq->rq.queue, dma_unmap_addr(&wq->rq, mapping)); - dma_free_coherent(&(rdev->lldi.pdev->dev), - wq->sq.memsize, wq->sq.queue, - dma_unmap_addr(&wq->sq, mapping)); + dealloc_sq(rdev, &wq->sq); c4iw_rqtpool_free(rdev, wq->rq.rqt_hwaddr, wq->rq.rqt_size); kfree(wq->rq.sw_rq); kfree(wq->sq.sw_sq); @@ -93,11 +148,12 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, if (!wq->rq.rqt_hwaddr) goto err4; - wq->sq.queue = dma_alloc_coherent(&(rdev->lldi.pdev->dev), - wq->sq.memsize, &(wq->sq.dma_addr), - GFP_KERNEL); - if (!wq->sq.queue) - goto err5; + if (user) { + if (alloc_oc_sq(rdev, &wq->sq) && alloc_host_sq(rdev, &wq->sq)) + goto err5; + } else + if (alloc_host_sq(rdev, &wq->sq)) + goto err5; memset(wq->sq.queue, 0, wq->sq.memsize); dma_unmap_addr_set(&wq->sq, mapping, wq->sq.dma_addr); @@ -144,7 +200,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, V_FW_RI_RES_WR_NRES(2) | FW_WR_COMPL(1)); res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16)); - res_wr->cookie = (u64)&wr_wait; + res_wr->cookie = (unsigned long) &wr_wait; res = res_wr->res; res->u.sqrq.restype = FW_RI_RES_TYPE_SQ; res->u.sqrq.op = FW_RI_RES_OP_WRITE; @@ -158,6 +214,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, V_FW_RI_RES_WR_HOSTFCMODE(0) | /* no host cidx updates */ V_FW_RI_RES_WR_CPRIO(0) | /* don't keep in chip cache */ V_FW_RI_RES_WR_PCIECHN(0) | /* set by uP at ri_init time */ + t4_sq_onchip(&wq->sq) ? F_FW_RI_RES_WR_ONCHIP : 0 | V_FW_RI_RES_WR_IQID(scq->cqid)); res->u.sqrq.dcaen_to_eqsize = cpu_to_be32( V_FW_RI_RES_WR_DCAEN(0) | @@ -198,14 +255,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, ret = c4iw_ofld_send(rdev, skb); if (ret) goto err7; - wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); - if (!wr_wait.done) { - printk(KERN_ERR MOD "Device %s not responding!\n", - pci_name(rdev->lldi.pdev)); - rdev->flags = T4_FATAL_ERROR; - ret = -EIO; - } else - ret = wr_wait.ret; + ret = c4iw_wait_for_reply(rdev, &wr_wait, 0, wq->sq.qid, __func__); if (ret) goto err7; @@ -219,9 +269,7 @@ err7: wq->rq.memsize, wq->rq.queue, dma_unmap_addr(&wq->rq, mapping)); err6: - dma_free_coherent(&(rdev->lldi.pdev->dev), - wq->sq.memsize, wq->sq.queue, - dma_unmap_addr(&wq->sq, mapping)); + dealloc_sq(rdev, &wq->sq); err5: c4iw_rqtpool_free(rdev, wq->rq.rqt_hwaddr, wq->rq.rqt_size); err4: @@ -263,6 +311,9 @@ static int build_immd(struct t4_sq *sq, struct fw_ri_immd *immdp, rem -= len; } } + len = roundup(plen + sizeof *immdp, 16) - (plen + sizeof *immdp); + if (len) + memset(dstp, 0, len); immdp->op = FW_RI_DATA_IMMD; immdp->r1 = 0; immdp->r2 = 0; @@ -292,6 +343,7 @@ static int build_isgl(__be64 *queue_start, __be64 *queue_end, if (++flitp == queue_end) flitp = queue_start; } + *flitp = (__force __be64)0; isglp->op = FW_RI_DATA_ISGL; isglp->r1 = 0; isglp->nsge = cpu_to_be16(num_sge); @@ -453,13 +505,15 @@ static int build_rdma_recv(struct c4iw_qp *qhp, union t4_recv_wr *wqe, return 0; } -static int build_fastreg(union t4_wr *wqe, struct ib_send_wr *wr, u8 *len16) +static int build_fastreg(struct t4_sq *sq, union t4_wr *wqe, + struct ib_send_wr *wr, u8 *len16) { struct fw_ri_immd *imdp; __be64 *p; int i; int pbllen = roundup(wr->wr.fast_reg.page_list_len * sizeof(u64), 32); + int rem; if (wr->wr.fast_reg.page_list_len > T4_MAX_FR_DEPTH) return -EINVAL; @@ -474,32 +528,28 @@ static int build_fastreg(union t4_wr *wqe, struct ib_send_wr *wr, u8 *len16) wqe->fr.va_hi = cpu_to_be32(wr->wr.fast_reg.iova_start >> 32); wqe->fr.va_lo_fbo = cpu_to_be32(wr->wr.fast_reg.iova_start & 0xffffffff); - if (pbllen > T4_MAX_FR_IMMD) { - struct c4iw_fr_page_list *c4pl = - to_c4iw_fr_page_list(wr->wr.fast_reg.page_list); - struct fw_ri_dsgl *sglp; - - sglp = (struct fw_ri_dsgl *)(&wqe->fr + 1); - sglp->op = FW_RI_DATA_DSGL; - sglp->r1 = 0; - sglp->nsge = cpu_to_be16(1); - sglp->addr0 = cpu_to_be64(c4pl->dma_addr); - sglp->len0 = cpu_to_be32(pbllen); - - *len16 = DIV_ROUND_UP(sizeof wqe->fr + sizeof *sglp, 16); - } else { - imdp = (struct fw_ri_immd *)(&wqe->fr + 1); - imdp->op = FW_RI_DATA_IMMD; - imdp->r1 = 0; - imdp->r2 = 0; - imdp->immdlen = cpu_to_be32(pbllen); - p = (__be64 *)(imdp + 1); - for (i = 0; i < wr->wr.fast_reg.page_list_len; i++, p++) - *p = cpu_to_be64( - (u64)wr->wr.fast_reg.page_list->page_list[i]); - *len16 = DIV_ROUND_UP(sizeof wqe->fr + sizeof *imdp + pbllen, - 16); + WARN_ON(pbllen > T4_MAX_FR_IMMD); + imdp = (struct fw_ri_immd *)(&wqe->fr + 1); + imdp->op = FW_RI_DATA_IMMD; + imdp->r1 = 0; + imdp->r2 = 0; + imdp->immdlen = cpu_to_be32(pbllen); + p = (__be64 *)(imdp + 1); + rem = pbllen; + for (i = 0; i < wr->wr.fast_reg.page_list_len; i++) { + *p = cpu_to_be64((u64)wr->wr.fast_reg.page_list->page_list[i]); + rem -= sizeof *p; + if (++p == (__be64 *)&sq->queue[sq->size]) + p = (__be64 *)sq->queue; } + BUG_ON(rem < 0); + while (rem) { + *p = 0; + rem -= sizeof *p; + if (++p == (__be64 *)&sq->queue[sq->size]) + p = (__be64 *)sq->queue; + } + *len16 = DIV_ROUND_UP(sizeof wqe->fr + sizeof *imdp + pbllen, 16); return 0; } @@ -587,7 +637,7 @@ int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, fw_opcode = FW_RI_RDMA_READ_WR; swsqe->opcode = FW_RI_READ_REQ; if (wr->opcode == IB_WR_RDMA_READ_WITH_INV) - fw_flags |= FW_RI_RDMA_READ_INVALIDATE; + fw_flags = FW_RI_RDMA_READ_INVALIDATE; else fw_flags = 0; err = build_rdma_read(wqe, wr, &len16); @@ -600,7 +650,7 @@ int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, case IB_WR_FAST_REG_MR: fw_opcode = FW_RI_FR_NSMR_WR; swsqe->opcode = FW_RI_FAST_REGISTER; - err = build_fastreg(wqe, wr, &len16); + err = build_fastreg(&qhp->wq.sq, wqe, wr, &len16); break; case IB_WR_LOCAL_INV: if (wr->send_flags & IB_SEND_FENCE) @@ -905,46 +955,38 @@ static void post_terminate(struct c4iw_qp *qhp, struct t4_cqe *err_cqe, * Assumes qhp lock is held. */ static void __flush_qp(struct c4iw_qp *qhp, struct c4iw_cq *rchp, - struct c4iw_cq *schp, unsigned long *flag) + struct c4iw_cq *schp) { int count; int flushed; + unsigned long flag; PDBG("%s qhp %p rchp %p schp %p\n", __func__, qhp, rchp, schp); - /* take a ref on the qhp since we must release the lock */ - atomic_inc(&qhp->refcnt); - spin_unlock_irqrestore(&qhp->lock, *flag); /* locking hierarchy: cq lock first, then qp lock. */ - spin_lock_irqsave(&rchp->lock, *flag); + spin_lock_irqsave(&rchp->lock, flag); spin_lock(&qhp->lock); c4iw_flush_hw_cq(&rchp->cq); c4iw_count_rcqes(&rchp->cq, &qhp->wq, &count); flushed = c4iw_flush_rq(&qhp->wq, &rchp->cq, count); spin_unlock(&qhp->lock); - spin_unlock_irqrestore(&rchp->lock, *flag); + spin_unlock_irqrestore(&rchp->lock, flag); if (flushed) (*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context); /* locking hierarchy: cq lock first, then qp lock. */ - spin_lock_irqsave(&schp->lock, *flag); + spin_lock_irqsave(&schp->lock, flag); spin_lock(&qhp->lock); c4iw_flush_hw_cq(&schp->cq); c4iw_count_scqes(&schp->cq, &qhp->wq, &count); flushed = c4iw_flush_sq(&qhp->wq, &schp->cq, count); spin_unlock(&qhp->lock); - spin_unlock_irqrestore(&schp->lock, *flag); + spin_unlock_irqrestore(&schp->lock, flag); if (flushed) (*schp->ibcq.comp_handler)(&schp->ibcq, schp->ibcq.cq_context); - - /* deref */ - if (atomic_dec_and_test(&qhp->refcnt)) - wake_up(&qhp->wait); - - spin_lock_irqsave(&qhp->lock, *flag); } -static void flush_qp(struct c4iw_qp *qhp, unsigned long *flag) +static void flush_qp(struct c4iw_qp *qhp) { struct c4iw_cq *rchp, *schp; @@ -958,7 +1000,7 @@ static void flush_qp(struct c4iw_qp *qhp, unsigned long *flag) t4_set_cq_in_error(&schp->cq); return; } - __flush_qp(qhp, rchp, schp, flag); + __flush_qp(qhp, rchp, schp); } static int rdma_fini(struct c4iw_dev *rhp, struct c4iw_qp *qhp, @@ -966,7 +1008,6 @@ static int rdma_fini(struct c4iw_dev *rhp, struct c4iw_qp *qhp, { struct fw_ri_wr *wqe; int ret; - struct c4iw_wr_wait wr_wait; struct sk_buff *skb; PDBG("%s qhp %p qid 0x%x tid %u\n", __func__, qhp, qhp->wq.sq.qid, @@ -985,28 +1026,16 @@ static int rdma_fini(struct c4iw_dev *rhp, struct c4iw_qp *qhp, wqe->flowid_len16 = cpu_to_be32( FW_WR_FLOWID(ep->hwtid) | FW_WR_LEN16(DIV_ROUND_UP(sizeof *wqe, 16))); - wqe->cookie = (u64)&wr_wait; + wqe->cookie = (unsigned long) &ep->com.wr_wait; wqe->u.fini.type = FW_RI_TYPE_FINI; - c4iw_init_wr_wait(&wr_wait); + c4iw_init_wr_wait(&ep->com.wr_wait); ret = c4iw_ofld_send(&rhp->rdev, skb); if (ret) goto out; - wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); - if (!wr_wait.done) { - printk(KERN_ERR MOD "Device %s not responding!\n", - pci_name(rhp->rdev.lldi.pdev)); - rhp->rdev.flags = T4_FATAL_ERROR; - ret = -EIO; - } else { - ret = wr_wait.ret; - if (ret) - printk(KERN_WARNING MOD - "%s: Abnormal close qpid %d ret %u\n", - pci_name(rhp->rdev.lldi.pdev), qhp->wq.sq.qid, - ret); - } + ret = c4iw_wait_for_reply(&rhp->rdev, &ep->com.wr_wait, qhp->ep->hwtid, + qhp->wq.sq.qid, __func__); out: PDBG("%s ret %d\n", __func__, ret); return ret; @@ -1040,7 +1069,6 @@ static int rdma_init(struct c4iw_dev *rhp, struct c4iw_qp *qhp) { struct fw_ri_wr *wqe; int ret; - struct c4iw_wr_wait wr_wait; struct sk_buff *skb; PDBG("%s qhp %p qid 0x%x tid %u\n", __func__, qhp, qhp->wq.sq.qid, @@ -1060,7 +1088,7 @@ static int rdma_init(struct c4iw_dev *rhp, struct c4iw_qp *qhp) FW_WR_FLOWID(qhp->ep->hwtid) | FW_WR_LEN16(DIV_ROUND_UP(sizeof *wqe, 16))); - wqe->cookie = (u64)&wr_wait; + wqe->cookie = (unsigned long) &qhp->ep->com.wr_wait; wqe->u.init.type = FW_RI_TYPE_INIT; wqe->u.init.mpareqbit_p2ptype = @@ -1097,19 +1125,13 @@ static int rdma_init(struct c4iw_dev *rhp, struct c4iw_qp *qhp) if (qhp->attr.mpa_attr.initiator) build_rtr_msg(qhp->attr.mpa_attr.p2p_type, &wqe->u.init); - c4iw_init_wr_wait(&wr_wait); + c4iw_init_wr_wait(&qhp->ep->com.wr_wait); ret = c4iw_ofld_send(&rhp->rdev, skb); if (ret) goto out; - wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); - if (!wr_wait.done) { - printk(KERN_ERR MOD "Device %s not responding!\n", - pci_name(rhp->rdev.lldi.pdev)); - rhp->rdev.flags = T4_FATAL_ERROR; - ret = -EIO; - } else - ret = wr_wait.ret; + ret = c4iw_wait_for_reply(&rhp->rdev, &qhp->ep->com.wr_wait, + qhp->ep->hwtid, qhp->wq.sq.qid, __func__); out: PDBG("%s ret %d\n", __func__, ret); return ret; @@ -1122,7 +1144,6 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp, { int ret = 0; struct c4iw_qp_attributes newattr = qhp->attr; - unsigned long flag; int disconnect = 0; int terminate = 0; int abort = 0; @@ -1133,7 +1154,7 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp, qhp, qhp->wq.sq.qid, qhp->wq.rq.qid, qhp->ep, qhp->attr.state, (mask & C4IW_QP_ATTR_NEXT_STATE) ? attrs->next_state : -1); - spin_lock_irqsave(&qhp->lock, flag); + mutex_lock(&qhp->mutex); /* Process attr changes if in IDLE */ if (mask & C4IW_QP_ATTR_VALID_MODIFY) { @@ -1184,7 +1205,7 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp, qhp->attr.mpa_attr = attrs->mpa_attr; qhp->attr.llp_stream_handle = attrs->llp_stream_handle; qhp->ep = qhp->attr.llp_stream_handle; - qhp->attr.state = C4IW_QP_STATE_RTS; + set_state(qhp, C4IW_QP_STATE_RTS); /* * Ref the endpoint here and deref when we @@ -1193,15 +1214,13 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp, * transition. */ c4iw_get_ep(&qhp->ep->com); - spin_unlock_irqrestore(&qhp->lock, flag); ret = rdma_init(rhp, qhp); - spin_lock_irqsave(&qhp->lock, flag); if (ret) goto err; break; case C4IW_QP_STATE_ERROR: - qhp->attr.state = C4IW_QP_STATE_ERROR; - flush_qp(qhp, &flag); + set_state(qhp, C4IW_QP_STATE_ERROR); + flush_qp(qhp); break; default: ret = -EINVAL; @@ -1212,38 +1231,38 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp, switch (attrs->next_state) { case C4IW_QP_STATE_CLOSING: BUG_ON(atomic_read(&qhp->ep->com.kref.refcount) < 2); - qhp->attr.state = C4IW_QP_STATE_CLOSING; + set_state(qhp, C4IW_QP_STATE_CLOSING); ep = qhp->ep; if (!internal) { abort = 0; disconnect = 1; - c4iw_get_ep(&ep->com); + c4iw_get_ep(&qhp->ep->com); } - spin_unlock_irqrestore(&qhp->lock, flag); ret = rdma_fini(rhp, qhp, ep); - spin_lock_irqsave(&qhp->lock, flag); if (ret) { - c4iw_get_ep(&ep->com); + if (internal) + c4iw_get_ep(&qhp->ep->com); disconnect = abort = 1; goto err; } break; case C4IW_QP_STATE_TERMINATE: - qhp->attr.state = C4IW_QP_STATE_TERMINATE; + set_state(qhp, C4IW_QP_STATE_TERMINATE); if (qhp->ibqp.uobject) t4_set_wq_in_error(&qhp->wq); ep = qhp->ep; - c4iw_get_ep(&ep->com); - terminate = 1; + if (!internal) + terminate = 1; disconnect = 1; + c4iw_get_ep(&qhp->ep->com); break; case C4IW_QP_STATE_ERROR: - qhp->attr.state = C4IW_QP_STATE_ERROR; + set_state(qhp, C4IW_QP_STATE_ERROR); if (!internal) { abort = 1; disconnect = 1; ep = qhp->ep; - c4iw_get_ep(&ep->com); + c4iw_get_ep(&qhp->ep->com); } goto err; break; @@ -1259,8 +1278,8 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp, } switch (attrs->next_state) { case C4IW_QP_STATE_IDLE: - flush_qp(qhp, &flag); - qhp->attr.state = C4IW_QP_STATE_IDLE; + flush_qp(qhp); + set_state(qhp, C4IW_QP_STATE_IDLE); qhp->attr.llp_stream_handle = NULL; c4iw_put_ep(&qhp->ep->com); qhp->ep = NULL; @@ -1282,7 +1301,7 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp, ret = -EINVAL; goto out; } - qhp->attr.state = C4IW_QP_STATE_IDLE; + set_state(qhp, C4IW_QP_STATE_IDLE); break; case C4IW_QP_STATE_TERMINATE: if (!internal) { @@ -1305,15 +1324,16 @@ err: /* disassociate the LLP connection */ qhp->attr.llp_stream_handle = NULL; - ep = qhp->ep; + if (!ep) + ep = qhp->ep; qhp->ep = NULL; - qhp->attr.state = C4IW_QP_STATE_ERROR; + set_state(qhp, C4IW_QP_STATE_ERROR); free = 1; wake_up(&qhp->wait); BUG_ON(!ep); - flush_qp(qhp, &flag); + flush_qp(qhp); out: - spin_unlock_irqrestore(&qhp->lock, flag); + mutex_unlock(&qhp->mutex); if (terminate) post_terminate(qhp, NULL, internal ? GFP_ATOMIC : GFP_KERNEL); @@ -1335,7 +1355,6 @@ out: */ if (free) c4iw_put_ep(&ep->com); - PDBG("%s exit state %d\n", __func__, qhp->attr.state); return ret; } @@ -1380,7 +1399,7 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, int sqsize, rqsize; struct c4iw_ucontext *ucontext; int ret; - struct c4iw_mm_entry *mm1, *mm2, *mm3, *mm4; + struct c4iw_mm_entry *mm1, *mm2, *mm3, *mm4, *mm5 = NULL; PDBG("%s ib_pd %p\n", __func__, pd); @@ -1450,6 +1469,7 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, qhp->attr.max_ord = 1; qhp->attr.max_ird = 1; spin_lock_init(&qhp->lock); + mutex_init(&qhp->mutex); init_waitqueue_head(&qhp->wait); atomic_set(&qhp->refcnt, 1); @@ -1478,7 +1498,15 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, ret = -ENOMEM; goto err6; } - + if (t4_sq_onchip(&qhp->wq.sq)) { + mm5 = kmalloc(sizeof *mm5, GFP_KERNEL); + if (!mm5) { + ret = -ENOMEM; + goto err7; + } + uresp.flags = C4IW_QPF_ONCHIP; + } else + uresp.flags = 0; uresp.qid_mask = rhp->rdev.qpmask; uresp.sqid = qhp->wq.sq.qid; uresp.sq_size = qhp->wq.sq.size; @@ -1487,6 +1515,10 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, uresp.rq_size = qhp->wq.rq.size; uresp.rq_memsize = qhp->wq.rq.memsize; spin_lock(&ucontext->mmap_lock); + if (mm5) { + uresp.ma_sync_key = ucontext->key; + ucontext->key += PAGE_SIZE; + } uresp.sq_key = ucontext->key; ucontext->key += PAGE_SIZE; uresp.rq_key = ucontext->key; @@ -1498,9 +1530,9 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, spin_unlock(&ucontext->mmap_lock); ret = ib_copy_to_udata(udata, &uresp, sizeof uresp); if (ret) - goto err7; + goto err8; mm1->key = uresp.sq_key; - mm1->addr = virt_to_phys(qhp->wq.sq.queue); + mm1->addr = qhp->wq.sq.phys_addr; mm1->len = PAGE_ALIGN(qhp->wq.sq.memsize); insert_mmap(ucontext, mm1); mm2->key = uresp.rq_key; @@ -1515,6 +1547,13 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, mm4->addr = qhp->wq.rq.udb; mm4->len = PAGE_SIZE; insert_mmap(ucontext, mm4); + if (mm5) { + mm5->key = uresp.ma_sync_key; + mm5->addr = (pci_resource_start(rhp->rdev.lldi.pdev, 0) + + A_PCIE_MA_SYNC) & PAGE_MASK; + mm5->len = PAGE_SIZE; + insert_mmap(ucontext, mm5); + } } qhp->ibqp.qp_num = qhp->wq.sq.qid; init_timer(&(qhp->timer)); @@ -1522,6 +1561,8 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, __func__, qhp, qhp->attr.sq_num_entries, qhp->attr.rq_num_entries, qhp->wq.sq.qid); return &qhp->ibqp; +err8: + kfree(mm5); err7: kfree(mm4); err6: diff --git a/drivers/infiniband/hw/cxgb4/resource.c b/drivers/infiniband/hw/cxgb4/resource.c index 83b23dfa250..4fb50d58b49 100644 --- a/drivers/infiniband/hw/cxgb4/resource.c +++ b/drivers/infiniband/hw/cxgb4/resource.c @@ -311,6 +311,9 @@ u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size) { unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size); PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size); + if (!addr && printk_ratelimit()) + printk(KERN_WARNING MOD "%s: Out of PBL memory\n", + pci_name(rdev->lldi.pdev)); return (u32)addr; } @@ -370,6 +373,9 @@ u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size) { unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6); PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size << 6); + if (!addr && printk_ratelimit()) + printk(KERN_WARNING MOD "%s: Out of RQT memory\n", + pci_name(rdev->lldi.pdev)); return (u32)addr; } @@ -416,3 +422,59 @@ void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev) { gen_pool_destroy(rdev->rqt_pool); } + +/* + * On-Chip QP Memory. + */ +#define MIN_OCQP_SHIFT 12 /* 4KB == min ocqp size */ + +u32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size) +{ + unsigned long addr = gen_pool_alloc(rdev->ocqp_pool, size); + PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size); + return (u32)addr; +} + +void c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size) +{ + PDBG("%s addr 0x%x size %d\n", __func__, addr, size); + gen_pool_free(rdev->ocqp_pool, (unsigned long)addr, size); +} + +int c4iw_ocqp_pool_create(struct c4iw_rdev *rdev) +{ + unsigned start, chunk, top; + + rdev->ocqp_pool = gen_pool_create(MIN_OCQP_SHIFT, -1); + if (!rdev->ocqp_pool) + return -ENOMEM; + + start = rdev->lldi.vr->ocq.start; + chunk = rdev->lldi.vr->ocq.size; + top = start + chunk; + + while (start < top) { + chunk = min(top - start + 1, chunk); + if (gen_pool_add(rdev->ocqp_pool, start, chunk, -1)) { + PDBG("%s failed to add OCQP chunk (%x/%x)\n", + __func__, start, chunk); + if (chunk <= 1024 << MIN_OCQP_SHIFT) { + printk(KERN_WARNING MOD + "Failed to add all OCQP chunks (%x/%x)\n", + start, top - start); + return 0; + } + chunk >>= 1; + } else { + PDBG("%s added OCQP chunk (%x/%x)\n", + __func__, start, chunk); + start += chunk; + } + } + return 0; +} + +void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev) +{ + gen_pool_destroy(rdev->ocqp_pool); +} diff --git a/drivers/infiniband/hw/cxgb4/t4.h b/drivers/infiniband/hw/cxgb4/t4.h index 24f369046ef..70004425d69 100644 --- a/drivers/infiniband/hw/cxgb4/t4.h +++ b/drivers/infiniband/hw/cxgb4/t4.h @@ -52,6 +52,7 @@ #define T4_STAG_UNSET 0xffffffff #define T4_FW_MAJ 0 #define T4_EQ_STATUS_ENTRIES (L1_CACHE_BYTES > 64 ? 2 : 1) +#define A_PCIE_MA_SYNC 0x30b4 struct t4_status_page { __be32 rsvd1; /* flit 0 - hw owns */ @@ -65,7 +66,7 @@ struct t4_status_page { #define T4_EQ_ENTRY_SIZE 64 -#define T4_SQ_NUM_SLOTS 4 +#define T4_SQ_NUM_SLOTS 5 #define T4_SQ_NUM_BYTES (T4_EQ_ENTRY_SIZE * T4_SQ_NUM_SLOTS) #define T4_MAX_SEND_SGE ((T4_SQ_NUM_BYTES - sizeof(struct fw_ri_send_wr) - \ sizeof(struct fw_ri_isgl)) / sizeof(struct fw_ri_sge)) @@ -78,7 +79,7 @@ struct t4_status_page { sizeof(struct fw_ri_rdma_write_wr) - \ sizeof(struct fw_ri_isgl)) / sizeof(struct fw_ri_sge)) #define T4_MAX_FR_IMMD ((T4_SQ_NUM_BYTES - sizeof(struct fw_ri_fr_nsmr_wr) - \ - sizeof(struct fw_ri_immd))) + sizeof(struct fw_ri_immd)) & ~31UL) #define T4_MAX_FR_DEPTH (T4_MAX_FR_IMMD / sizeof(u64)) #define T4_RQ_NUM_SLOTS 2 @@ -266,10 +267,36 @@ struct t4_swsqe { u16 idx; }; +static inline pgprot_t t4_pgprot_wc(pgprot_t prot) +{ +#if defined(__i386__) || defined(__x86_64__) + return pgprot_writecombine(prot); +#elif defined(CONFIG_PPC64) + return __pgprot((pgprot_val(prot) | _PAGE_NO_CACHE) & + ~(pgprot_t)_PAGE_GUARDED); +#else + return pgprot_noncached(prot); +#endif +} + +static inline int t4_ocqp_supported(void) +{ +#if defined(__i386__) || defined(__x86_64__) || defined(CONFIG_PPC64) + return 1; +#else + return 0; +#endif +} + +enum { + T4_SQ_ONCHIP = (1<<0), +}; + struct t4_sq { union t4_wr *queue; dma_addr_t dma_addr; DEFINE_DMA_UNMAP_ADDR(mapping); + unsigned long phys_addr; struct t4_swsqe *sw_sq; struct t4_swsqe *oldest_read; u64 udb; @@ -280,6 +307,7 @@ struct t4_sq { u16 cidx; u16 pidx; u16 wq_pidx; + u16 flags; }; struct t4_swrqe { @@ -350,6 +378,11 @@ static inline void t4_rq_consume(struct t4_wq *wq) wq->rq.cidx = 0; } +static inline int t4_sq_onchip(struct t4_sq *sq) +{ + return sq->flags & T4_SQ_ONCHIP; +} + static inline int t4_sq_empty(struct t4_wq *wq) { return wq->sq.in_use == 0; @@ -396,30 +429,27 @@ static inline void t4_ring_rq_db(struct t4_wq *wq, u16 inc) static inline int t4_wq_in_error(struct t4_wq *wq) { - return wq->sq.queue[wq->sq.size].status.qp_err; + return wq->rq.queue[wq->rq.size].status.qp_err; } static inline void t4_set_wq_in_error(struct t4_wq *wq) { - wq->sq.queue[wq->sq.size].status.qp_err = 1; wq->rq.queue[wq->rq.size].status.qp_err = 1; } static inline void t4_disable_wq_db(struct t4_wq *wq) { - wq->sq.queue[wq->sq.size].status.db_off = 1; wq->rq.queue[wq->rq.size].status.db_off = 1; } static inline void t4_enable_wq_db(struct t4_wq *wq) { - wq->sq.queue[wq->sq.size].status.db_off = 0; wq->rq.queue[wq->rq.size].status.db_off = 0; } static inline int t4_wq_db_enabled(struct t4_wq *wq) { - return !wq->sq.queue[wq->sq.size].status.db_off; + return !wq->rq.queue[wq->rq.size].status.db_off; } struct t4_cq { diff --git a/drivers/infiniband/hw/cxgb4/user.h b/drivers/infiniband/hw/cxgb4/user.h index ed6414abde0..e6669d54770 100644 --- a/drivers/infiniband/hw/cxgb4/user.h +++ b/drivers/infiniband/hw/cxgb4/user.h @@ -50,7 +50,13 @@ struct c4iw_create_cq_resp { __u32 qid_mask; }; + +enum { + C4IW_QPF_ONCHIP = (1<<0) +}; + struct c4iw_create_qp_resp { + __u64 ma_sync_key; __u64 sq_key; __u64 rq_key; __u64 sq_db_gts_key; @@ -62,5 +68,6 @@ struct c4iw_create_qp_resp { __u32 sq_size; __u32 rq_size; __u32 qid_mask; + __u32 flags; }; #endif diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.c b/drivers/infiniband/hw/ehca/ehca_mrmw.c index 53f4cd4fc19..43cae84005f 100644 --- a/drivers/infiniband/hw/ehca/ehca_mrmw.c +++ b/drivers/infiniband/hw/ehca/ehca_mrmw.c @@ -171,7 +171,7 @@ struct ib_mr *ehca_get_dma_mr(struct ib_pd *pd, int mr_access_flags) } ret = ehca_reg_maxmr(shca, e_maxmr, - (void *)ehca_map_vaddr((void *)KERNELBASE), + (void *)ehca_map_vaddr((void *)(KERNELBASE + PHYSICAL_START)), mr_access_flags, e_pd, &e_maxmr->ib.ib_mr.lkey, &e_maxmr->ib.ib_mr.rkey); @@ -1636,7 +1636,7 @@ int ehca_reg_internal_maxmr( /* register internal max-MR on HCA */ size_maxmr = ehca_mr_len; - iova_start = (u64 *)ehca_map_vaddr((void *)KERNELBASE); + iova_start = (u64 *)ehca_map_vaddr((void *)(KERNELBASE + PHYSICAL_START)); ib_pbuf.addr = 0; ib_pbuf.size = size_maxmr; num_kpages = NUM_CHUNKS(((u64)iova_start % PAGE_SIZE) + size_maxmr, @@ -2209,7 +2209,7 @@ int ehca_mr_is_maxmr(u64 size, { /* a MR is treated as max-MR only if it fits following: */ if ((size == ehca_mr_len) && - (iova_start == (void *)ehca_map_vaddr((void *)KERNELBASE))) { + (iova_start == (void *)ehca_map_vaddr((void *)(KERNELBASE + PHYSICAL_START)))) { ehca_gen_dbg("this is a max-MR"); return 1; } else diff --git a/drivers/infiniband/hw/ipath/Makefile b/drivers/infiniband/hw/ipath/Makefile index fa3df82681d..4496f2820c9 100644 --- a/drivers/infiniband/hw/ipath/Makefile +++ b/drivers/infiniband/hw/ipath/Makefile @@ -1,4 +1,4 @@ -EXTRA_CFLAGS += -DIPATH_IDSTR='"QLogic kernel.org driver"' \ +ccflags-y := -DIPATH_IDSTR='"QLogic kernel.org driver"' \ -DIPATH_KERN_TYPE=0 obj-$(CONFIG_INFINIBAND_IPATH) += ib_ipath.o diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c index 11a236f8d88..4b8f9c49397 100644 --- a/drivers/infiniband/hw/mlx4/ah.c +++ b/drivers/infiniband/hw/mlx4/ah.c @@ -30,66 +30,163 @@ * SOFTWARE. */ +#include <rdma/ib_addr.h> +#include <rdma/ib_cache.h> + #include <linux/slab.h> +#include <linux/inet.h> +#include <linux/string.h> #include "mlx4_ib.h" -struct ib_ah *mlx4_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr) +int mlx4_ib_resolve_grh(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah_attr, + u8 *mac, int *is_mcast, u8 port) { - struct mlx4_dev *dev = to_mdev(pd->device)->dev; - struct mlx4_ib_ah *ah; + struct in6_addr in6; - ah = kmalloc(sizeof *ah, GFP_ATOMIC); - if (!ah) - return ERR_PTR(-ENOMEM); + *is_mcast = 0; - memset(&ah->av, 0, sizeof ah->av); + memcpy(&in6, ah_attr->grh.dgid.raw, sizeof in6); + if (rdma_link_local_addr(&in6)) + rdma_get_ll_mac(&in6, mac); + else if (rdma_is_multicast_addr(&in6)) { + rdma_get_mcast_mac(&in6, mac); + *is_mcast = 1; + } else + return -EINVAL; - ah->av.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24)); - ah->av.g_slid = ah_attr->src_path_bits; - ah->av.dlid = cpu_to_be16(ah_attr->dlid); - if (ah_attr->static_rate) { - ah->av.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET; - while (ah->av.stat_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET && - !(1 << ah->av.stat_rate & dev->caps.stat_rate_support)) - --ah->av.stat_rate; - } - ah->av.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28); + return 0; +} + +static struct ib_ah *create_ib_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr, + struct mlx4_ib_ah *ah) +{ + struct mlx4_dev *dev = to_mdev(pd->device)->dev; + + ah->av.ib.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24)); + ah->av.ib.g_slid = ah_attr->src_path_bits; if (ah_attr->ah_flags & IB_AH_GRH) { - ah->av.g_slid |= 0x80; - ah->av.gid_index = ah_attr->grh.sgid_index; - ah->av.hop_limit = ah_attr->grh.hop_limit; - ah->av.sl_tclass_flowlabel |= + ah->av.ib.g_slid |= 0x80; + ah->av.ib.gid_index = ah_attr->grh.sgid_index; + ah->av.ib.hop_limit = ah_attr->grh.hop_limit; + ah->av.ib.sl_tclass_flowlabel |= cpu_to_be32((ah_attr->grh.traffic_class << 20) | ah_attr->grh.flow_label); - memcpy(ah->av.dgid, ah_attr->grh.dgid.raw, 16); + memcpy(ah->av.ib.dgid, ah_attr->grh.dgid.raw, 16); + } + + ah->av.ib.dlid = cpu_to_be16(ah_attr->dlid); + if (ah_attr->static_rate) { + ah->av.ib.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET; + while (ah->av.ib.stat_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET && + !(1 << ah->av.ib.stat_rate & dev->caps.stat_rate_support)) + --ah->av.ib.stat_rate; } + ah->av.ib.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28); return &ah->ibah; } +static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr, + struct mlx4_ib_ah *ah) +{ + struct mlx4_ib_dev *ibdev = to_mdev(pd->device); + struct mlx4_dev *dev = ibdev->dev; + union ib_gid sgid; + u8 mac[6]; + int err; + int is_mcast; + u16 vlan_tag; + + err = mlx4_ib_resolve_grh(ibdev, ah_attr, mac, &is_mcast, ah_attr->port_num); + if (err) + return ERR_PTR(err); + + memcpy(ah->av.eth.mac, mac, 6); + err = ib_get_cached_gid(pd->device, ah_attr->port_num, ah_attr->grh.sgid_index, &sgid); + if (err) + return ERR_PTR(err); + vlan_tag = rdma_get_vlan_id(&sgid); + if (vlan_tag < 0x1000) + vlan_tag |= (ah_attr->sl & 7) << 13; + ah->av.eth.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24)); + ah->av.eth.gid_index = ah_attr->grh.sgid_index; + ah->av.eth.vlan = cpu_to_be16(vlan_tag); + if (ah_attr->static_rate) { + ah->av.eth.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET; + while (ah->av.eth.stat_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET && + !(1 << ah->av.eth.stat_rate & dev->caps.stat_rate_support)) + --ah->av.eth.stat_rate; + } + + /* + * HW requires multicast LID so we just choose one. + */ + if (is_mcast) + ah->av.ib.dlid = cpu_to_be16(0xc000); + + memcpy(ah->av.eth.dgid, ah_attr->grh.dgid.raw, 16); + ah->av.eth.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28); + + return &ah->ibah; +} + +struct ib_ah *mlx4_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr) +{ + struct mlx4_ib_ah *ah; + struct ib_ah *ret; + + ah = kzalloc(sizeof *ah, GFP_ATOMIC); + if (!ah) + return ERR_PTR(-ENOMEM); + + if (rdma_port_get_link_layer(pd->device, ah_attr->port_num) == IB_LINK_LAYER_ETHERNET) { + if (!(ah_attr->ah_flags & IB_AH_GRH)) { + ret = ERR_PTR(-EINVAL); + } else { + /* + * TBD: need to handle the case when we get + * called in an atomic context and there we + * might sleep. We don't expect this + * currently since we're working with link + * local addresses which we can translate + * without going to sleep. + */ + ret = create_iboe_ah(pd, ah_attr, ah); + } + + if (IS_ERR(ret)) + kfree(ah); + + return ret; + } else + return create_ib_ah(pd, ah_attr, ah); /* never fails */ +} + int mlx4_ib_query_ah(struct ib_ah *ibah, struct ib_ah_attr *ah_attr) { struct mlx4_ib_ah *ah = to_mah(ibah); + enum rdma_link_layer ll; memset(ah_attr, 0, sizeof *ah_attr); - ah_attr->dlid = be16_to_cpu(ah->av.dlid); - ah_attr->sl = be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 28; - ah_attr->port_num = be32_to_cpu(ah->av.port_pd) >> 24; - if (ah->av.stat_rate) - ah_attr->static_rate = ah->av.stat_rate - MLX4_STAT_RATE_OFFSET; - ah_attr->src_path_bits = ah->av.g_slid & 0x7F; + ah_attr->sl = be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 28; + ah_attr->port_num = be32_to_cpu(ah->av.ib.port_pd) >> 24; + ll = rdma_port_get_link_layer(ibah->device, ah_attr->port_num); + ah_attr->dlid = ll == IB_LINK_LAYER_INFINIBAND ? be16_to_cpu(ah->av.ib.dlid) : 0; + if (ah->av.ib.stat_rate) + ah_attr->static_rate = ah->av.ib.stat_rate - MLX4_STAT_RATE_OFFSET; + ah_attr->src_path_bits = ah->av.ib.g_slid & 0x7F; if (mlx4_ib_ah_grh_present(ah)) { ah_attr->ah_flags = IB_AH_GRH; ah_attr->grh.traffic_class = - be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 20; + be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 20; ah_attr->grh.flow_label = - be32_to_cpu(ah->av.sl_tclass_flowlabel) & 0xfffff; - ah_attr->grh.hop_limit = ah->av.hop_limit; - ah_attr->grh.sgid_index = ah->av.gid_index; - memcpy(ah_attr->grh.dgid.raw, ah->av.dgid, 16); + be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) & 0xfffff; + ah_attr->grh.hop_limit = ah->av.ib.hop_limit; + ah_attr->grh.sgid_index = ah->av.ib.gid_index; + memcpy(ah_attr->grh.dgid.raw, ah->av.ib.dgid, 16); } return 0; diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index f38d5b11892..c9a8dd63b9e 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -311,19 +311,25 @@ int mlx4_ib_mad_init(struct mlx4_ib_dev *dev) struct ib_mad_agent *agent; int p, q; int ret; + enum rdma_link_layer ll; - for (p = 0; p < dev->num_ports; ++p) + for (p = 0; p < dev->num_ports; ++p) { + ll = rdma_port_get_link_layer(&dev->ib_dev, p + 1); for (q = 0; q <= 1; ++q) { - agent = ib_register_mad_agent(&dev->ib_dev, p + 1, - q ? IB_QPT_GSI : IB_QPT_SMI, - NULL, 0, send_handler, - NULL, NULL); - if (IS_ERR(agent)) { - ret = PTR_ERR(agent); - goto err; - } - dev->send_agent[p][q] = agent; + if (ll == IB_LINK_LAYER_INFINIBAND) { + agent = ib_register_mad_agent(&dev->ib_dev, p + 1, + q ? IB_QPT_GSI : IB_QPT_SMI, + NULL, 0, send_handler, + NULL, NULL); + if (IS_ERR(agent)) { + ret = PTR_ERR(agent); + goto err; + } + dev->send_agent[p][q] = agent; + } else + dev->send_agent[p][q] = NULL; } + } return 0; @@ -344,8 +350,10 @@ void mlx4_ib_mad_cleanup(struct mlx4_ib_dev *dev) for (p = 0; p < dev->num_ports; ++p) { for (q = 0; q <= 1; ++q) { agent = dev->send_agent[p][q]; - dev->send_agent[p][q] = NULL; - ib_unregister_mad_agent(agent); + if (agent) { + dev->send_agent[p][q] = NULL; + ib_unregister_mad_agent(agent); + } } if (dev->sm_ah[p]) diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 4e94e360e43..bf3e20cd029 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -35,9 +35,14 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/rtnetlink.h> +#include <linux/if_vlan.h> #include <rdma/ib_smi.h> #include <rdma/ib_user_verbs.h> +#include <rdma/ib_addr.h> #include <linux/mlx4/driver.h> #include <linux/mlx4/cmd.h> @@ -58,6 +63,15 @@ static const char mlx4_ib_version[] = DRV_NAME ": Mellanox ConnectX InfiniBand driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; +struct update_gid_work { + struct work_struct work; + union ib_gid gids[128]; + struct mlx4_ib_dev *dev; + int port; +}; + +static struct workqueue_struct *wq; + static void init_query_mad(struct ib_smp *mad) { mad->base_version = 1; @@ -66,6 +80,8 @@ static void init_query_mad(struct ib_smp *mad) mad->method = IB_MGMT_METHOD_GET; } +static union ib_gid zgid; + static int mlx4_ib_query_device(struct ib_device *ibdev, struct ib_device_attr *props) { @@ -135,7 +151,7 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->max_srq = dev->dev->caps.num_srqs - dev->dev->caps.reserved_srqs; props->max_srq_wr = dev->dev->caps.max_srq_wqes - 1; props->max_srq_sge = dev->dev->caps.max_srq_sge; - props->max_fast_reg_page_list_len = PAGE_SIZE / sizeof (u64); + props->max_fast_reg_page_list_len = MLX4_MAX_FAST_REG_PAGES; props->local_ca_ack_delay = dev->dev->caps.local_ca_ack_delay; props->atomic_cap = dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_ATOMIC ? IB_ATOMIC_HCA : IB_ATOMIC_NONE; @@ -154,28 +170,19 @@ out: return err; } -static int mlx4_ib_query_port(struct ib_device *ibdev, u8 port, - struct ib_port_attr *props) +static enum rdma_link_layer +mlx4_ib_port_link_layer(struct ib_device *device, u8 port_num) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; - int err = -ENOMEM; - - in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); - out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL); - if (!in_mad || !out_mad) - goto out; - - memset(props, 0, sizeof *props); - - init_query_mad(in_mad); - in_mad->attr_id = IB_SMP_ATTR_PORT_INFO; - in_mad->attr_mod = cpu_to_be32(port); + struct mlx4_dev *dev = to_mdev(device)->dev; - err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad); - if (err) - goto out; + return dev->caps.port_mask & (1 << (port_num - 1)) ? + IB_LINK_LAYER_INFINIBAND : IB_LINK_LAYER_ETHERNET; +} +static int ib_link_query_port(struct ib_device *ibdev, u8 port, + struct ib_port_attr *props, + struct ib_smp *out_mad) +{ props->lid = be16_to_cpup((__be16 *) (out_mad->data + 16)); props->lmc = out_mad->data[34] & 0x7; props->sm_lid = be16_to_cpup((__be16 *) (out_mad->data + 18)); @@ -196,6 +203,80 @@ static int mlx4_ib_query_port(struct ib_device *ibdev, u8 port, props->max_vl_num = out_mad->data[37] >> 4; props->init_type_reply = out_mad->data[41] >> 4; + return 0; +} + +static u8 state_to_phys_state(enum ib_port_state state) +{ + return state == IB_PORT_ACTIVE ? 5 : 3; +} + +static int eth_link_query_port(struct ib_device *ibdev, u8 port, + struct ib_port_attr *props, + struct ib_smp *out_mad) +{ + struct mlx4_ib_iboe *iboe = &to_mdev(ibdev)->iboe; + struct net_device *ndev; + enum ib_mtu tmp; + + props->active_width = IB_WIDTH_4X; + props->active_speed = 4; + props->port_cap_flags = IB_PORT_CM_SUP; + props->gid_tbl_len = to_mdev(ibdev)->dev->caps.gid_table_len[port]; + props->max_msg_sz = to_mdev(ibdev)->dev->caps.max_msg_sz; + props->pkey_tbl_len = 1; + props->bad_pkey_cntr = be16_to_cpup((__be16 *) (out_mad->data + 46)); + props->qkey_viol_cntr = be16_to_cpup((__be16 *) (out_mad->data + 48)); + props->max_mtu = IB_MTU_2048; + props->subnet_timeout = 0; + props->max_vl_num = out_mad->data[37] >> 4; + props->init_type_reply = 0; + props->state = IB_PORT_DOWN; + props->phys_state = state_to_phys_state(props->state); + props->active_mtu = IB_MTU_256; + spin_lock(&iboe->lock); + ndev = iboe->netdevs[port - 1]; + if (!ndev) + goto out; + + tmp = iboe_get_mtu(ndev->mtu); + props->active_mtu = tmp ? min(props->max_mtu, tmp) : IB_MTU_256; + + props->state = netif_running(ndev) && netif_oper_up(ndev) ? + IB_PORT_ACTIVE : IB_PORT_DOWN; + props->phys_state = state_to_phys_state(props->state); + +out: + spin_unlock(&iboe->lock); + return 0; +} + +static int mlx4_ib_query_port(struct ib_device *ibdev, u8 port, + struct ib_port_attr *props) +{ + struct ib_smp *in_mad = NULL; + struct ib_smp *out_mad = NULL; + int err = -ENOMEM; + + in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); + out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL); + if (!in_mad || !out_mad) + goto out; + + memset(props, 0, sizeof *props); + + init_query_mad(in_mad); + in_mad->attr_id = IB_SMP_ATTR_PORT_INFO; + in_mad->attr_mod = cpu_to_be32(port); + + err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad); + if (err) + goto out; + + err = mlx4_ib_port_link_layer(ibdev, port) == IB_LINK_LAYER_INFINIBAND ? + ib_link_query_port(ibdev, port, props, out_mad) : + eth_link_query_port(ibdev, port, props, out_mad); + out: kfree(in_mad); kfree(out_mad); @@ -203,8 +284,8 @@ out: return err; } -static int mlx4_ib_query_gid(struct ib_device *ibdev, u8 port, int index, - union ib_gid *gid) +static int __mlx4_ib_query_gid(struct ib_device *ibdev, u8 port, int index, + union ib_gid *gid) { struct ib_smp *in_mad = NULL; struct ib_smp *out_mad = NULL; @@ -241,6 +322,25 @@ out: return err; } +static int iboe_query_gid(struct ib_device *ibdev, u8 port, int index, + union ib_gid *gid) +{ + struct mlx4_ib_dev *dev = to_mdev(ibdev); + + *gid = dev->iboe.gid_table[port - 1][index]; + + return 0; +} + +static int mlx4_ib_query_gid(struct ib_device *ibdev, u8 port, int index, + union ib_gid *gid) +{ + if (rdma_port_get_link_layer(ibdev, port) == IB_LINK_LAYER_INFINIBAND) + return __mlx4_ib_query_gid(ibdev, port, index, gid); + else + return iboe_query_gid(ibdev, port, index, gid); +} + static int mlx4_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey) { @@ -272,14 +372,32 @@ out: static int mlx4_ib_modify_device(struct ib_device *ibdev, int mask, struct ib_device_modify *props) { + struct mlx4_cmd_mailbox *mailbox; + if (mask & ~IB_DEVICE_MODIFY_NODE_DESC) return -EOPNOTSUPP; - if (mask & IB_DEVICE_MODIFY_NODE_DESC) { - spin_lock(&to_mdev(ibdev)->sm_lock); - memcpy(ibdev->node_desc, props->node_desc, 64); - spin_unlock(&to_mdev(ibdev)->sm_lock); - } + if (!(mask & IB_DEVICE_MODIFY_NODE_DESC)) + return 0; + + spin_lock(&to_mdev(ibdev)->sm_lock); + memcpy(ibdev->node_desc, props->node_desc, 64); + spin_unlock(&to_mdev(ibdev)->sm_lock); + + /* + * If possible, pass node desc to FW, so it can generate + * a 144 trap. If cmd fails, just ignore. + */ + mailbox = mlx4_alloc_cmd_mailbox(to_mdev(ibdev)->dev); + if (IS_ERR(mailbox)) + return 0; + + memset(mailbox->buf, 0, 256); + memcpy(mailbox->buf, props->node_desc, 64); + mlx4_cmd(to_mdev(ibdev)->dev, mailbox->dma, 1, 0, + MLX4_CMD_SET_NODE, MLX4_CMD_TIME_CLASS_A); + + mlx4_free_cmd_mailbox(to_mdev(ibdev)->dev, mailbox); return 0; } @@ -289,6 +407,7 @@ static int mlx4_SET_PORT(struct mlx4_ib_dev *dev, u8 port, int reset_qkey_viols, { struct mlx4_cmd_mailbox *mailbox; int err; + u8 is_eth = dev->dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH; mailbox = mlx4_alloc_cmd_mailbox(dev->dev); if (IS_ERR(mailbox)) @@ -304,7 +423,7 @@ static int mlx4_SET_PORT(struct mlx4_ib_dev *dev, u8 port, int reset_qkey_viols, ((__be32 *) mailbox->buf)[1] = cpu_to_be32(cap_mask); } - err = mlx4_cmd(dev->dev, mailbox->dma, port, 0, MLX4_CMD_SET_PORT, + err = mlx4_cmd(dev->dev, mailbox->dma, port, is_eth, MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B); mlx4_free_cmd_mailbox(dev->dev, mailbox); @@ -447,18 +566,132 @@ static int mlx4_ib_dealloc_pd(struct ib_pd *pd) return 0; } +static int add_gid_entry(struct ib_qp *ibqp, union ib_gid *gid) +{ + struct mlx4_ib_qp *mqp = to_mqp(ibqp); + struct mlx4_ib_dev *mdev = to_mdev(ibqp->device); + struct mlx4_ib_gid_entry *ge; + + ge = kzalloc(sizeof *ge, GFP_KERNEL); + if (!ge) + return -ENOMEM; + + ge->gid = *gid; + if (mlx4_ib_add_mc(mdev, mqp, gid)) { + ge->port = mqp->port; + ge->added = 1; + } + + mutex_lock(&mqp->mutex); + list_add_tail(&ge->list, &mqp->gid_list); + mutex_unlock(&mqp->mutex); + + return 0; +} + +int mlx4_ib_add_mc(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp, + union ib_gid *gid) +{ + u8 mac[6]; + struct net_device *ndev; + int ret = 0; + + if (!mqp->port) + return 0; + + spin_lock(&mdev->iboe.lock); + ndev = mdev->iboe.netdevs[mqp->port - 1]; + if (ndev) + dev_hold(ndev); + spin_unlock(&mdev->iboe.lock); + + if (ndev) { + rdma_get_mcast_mac((struct in6_addr *)gid, mac); + rtnl_lock(); + dev_mc_add(mdev->iboe.netdevs[mqp->port - 1], mac); + ret = 1; + rtnl_unlock(); + dev_put(ndev); + } + + return ret; +} + static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) { - return mlx4_multicast_attach(to_mdev(ibqp->device)->dev, - &to_mqp(ibqp)->mqp, gid->raw, - !!(to_mqp(ibqp)->flags & - MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK)); + int err; + struct mlx4_ib_dev *mdev = to_mdev(ibqp->device); + struct mlx4_ib_qp *mqp = to_mqp(ibqp); + + err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, !!(mqp->flags & + MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK)); + if (err) + return err; + + err = add_gid_entry(ibqp, gid); + if (err) + goto err_add; + + return 0; + +err_add: + mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw); + return err; +} + +static struct mlx4_ib_gid_entry *find_gid_entry(struct mlx4_ib_qp *qp, u8 *raw) +{ + struct mlx4_ib_gid_entry *ge; + struct mlx4_ib_gid_entry *tmp; + struct mlx4_ib_gid_entry *ret = NULL; + + list_for_each_entry_safe(ge, tmp, &qp->gid_list, list) { + if (!memcmp(raw, ge->gid.raw, 16)) { + ret = ge; + break; + } + } + + return ret; } static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) { - return mlx4_multicast_detach(to_mdev(ibqp->device)->dev, - &to_mqp(ibqp)->mqp, gid->raw); + int err; + struct mlx4_ib_dev *mdev = to_mdev(ibqp->device); + struct mlx4_ib_qp *mqp = to_mqp(ibqp); + u8 mac[6]; + struct net_device *ndev; + struct mlx4_ib_gid_entry *ge; + + err = mlx4_multicast_detach(mdev->dev, + &mqp->mqp, gid->raw); + if (err) + return err; + + mutex_lock(&mqp->mutex); + ge = find_gid_entry(mqp, gid->raw); + if (ge) { + spin_lock(&mdev->iboe.lock); + ndev = ge->added ? mdev->iboe.netdevs[ge->port - 1] : NULL; + if (ndev) + dev_hold(ndev); + spin_unlock(&mdev->iboe.lock); + rdma_get_mcast_mac((struct in6_addr *)gid, mac); + if (ndev) { + rtnl_lock(); + dev_mc_del(mdev->iboe.netdevs[ge->port - 1], mac); + rtnl_unlock(); + dev_put(ndev); + } + list_del(&ge->list); + kfree(ge); + } else + printk(KERN_WARNING "could not find mgid entry\n"); + + mutex_unlock(&mqp->mutex); + + return 0; } static int init_node_data(struct mlx4_ib_dev *dev) @@ -543,15 +776,215 @@ static struct device_attribute *mlx4_class_attributes[] = { &dev_attr_board_id }; +static void mlx4_addrconf_ifid_eui48(u8 *eui, u16 vlan_id, struct net_device *dev) +{ + memcpy(eui, dev->dev_addr, 3); + memcpy(eui + 5, dev->dev_addr + 3, 3); + if (vlan_id < 0x1000) { + eui[3] = vlan_id >> 8; + eui[4] = vlan_id & 0xff; + } else { + eui[3] = 0xff; + eui[4] = 0xfe; + } + eui[0] ^= 2; +} + +static void update_gids_task(struct work_struct *work) +{ + struct update_gid_work *gw = container_of(work, struct update_gid_work, work); + struct mlx4_cmd_mailbox *mailbox; + union ib_gid *gids; + int err; + struct mlx4_dev *dev = gw->dev->dev; + struct ib_event event; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) { + printk(KERN_WARNING "update gid table failed %ld\n", PTR_ERR(mailbox)); + return; + } + + gids = mailbox->buf; + memcpy(gids, gw->gids, sizeof gw->gids); + + err = mlx4_cmd(dev, mailbox->dma, MLX4_SET_PORT_GID_TABLE << 8 | gw->port, + 1, MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B); + if (err) + printk(KERN_WARNING "set port command failed\n"); + else { + memcpy(gw->dev->iboe.gid_table[gw->port - 1], gw->gids, sizeof gw->gids); + event.device = &gw->dev->ib_dev; + event.element.port_num = gw->port; + event.event = IB_EVENT_LID_CHANGE; + ib_dispatch_event(&event); + } + + mlx4_free_cmd_mailbox(dev, mailbox); + kfree(gw); +} + +static int update_ipv6_gids(struct mlx4_ib_dev *dev, int port, int clear) +{ + struct net_device *ndev = dev->iboe.netdevs[port - 1]; + struct update_gid_work *work; + struct net_device *tmp; + int i; + u8 *hits; + int ret; + union ib_gid gid; + int free; + int found; + int need_update = 0; + u16 vid; + + work = kzalloc(sizeof *work, GFP_ATOMIC); + if (!work) + return -ENOMEM; + + hits = kzalloc(128, GFP_ATOMIC); + if (!hits) { + ret = -ENOMEM; + goto out; + } + + read_lock(&dev_base_lock); + for_each_netdev(&init_net, tmp) { + if (ndev && (tmp == ndev || rdma_vlan_dev_real_dev(tmp) == ndev)) { + gid.global.subnet_prefix = cpu_to_be64(0xfe80000000000000LL); + vid = rdma_vlan_dev_vlan_id(tmp); + mlx4_addrconf_ifid_eui48(&gid.raw[8], vid, ndev); + found = 0; + free = -1; + for (i = 0; i < 128; ++i) { + if (free < 0 && + !memcmp(&dev->iboe.gid_table[port - 1][i], &zgid, sizeof zgid)) + free = i; + if (!memcmp(&dev->iboe.gid_table[port - 1][i], &gid, sizeof gid)) { + hits[i] = 1; + found = 1; + break; + } + } + + if (!found) { + if (tmp == ndev && + (memcmp(&dev->iboe.gid_table[port - 1][0], + &gid, sizeof gid) || + !memcmp(&dev->iboe.gid_table[port - 1][0], + &zgid, sizeof gid))) { + dev->iboe.gid_table[port - 1][0] = gid; + ++need_update; + hits[0] = 1; + } else if (free >= 0) { + dev->iboe.gid_table[port - 1][free] = gid; + hits[free] = 1; + ++need_update; + } + } + } + } + read_unlock(&dev_base_lock); + + for (i = 0; i < 128; ++i) + if (!hits[i]) { + if (memcmp(&dev->iboe.gid_table[port - 1][i], &zgid, sizeof zgid)) + ++need_update; + dev->iboe.gid_table[port - 1][i] = zgid; + } + + if (need_update) { + memcpy(work->gids, dev->iboe.gid_table[port - 1], sizeof work->gids); + INIT_WORK(&work->work, update_gids_task); + work->port = port; + work->dev = dev; + queue_work(wq, &work->work); + } else + kfree(work); + + kfree(hits); + return 0; + +out: + kfree(work); + return ret; +} + +static void handle_en_event(struct mlx4_ib_dev *dev, int port, unsigned long event) +{ + switch (event) { + case NETDEV_UP: + case NETDEV_CHANGEADDR: + update_ipv6_gids(dev, port, 0); + break; + + case NETDEV_DOWN: + update_ipv6_gids(dev, port, 1); + dev->iboe.netdevs[port - 1] = NULL; + } +} + +static void netdev_added(struct mlx4_ib_dev *dev, int port) +{ + update_ipv6_gids(dev, port, 0); +} + +static void netdev_removed(struct mlx4_ib_dev *dev, int port) +{ + update_ipv6_gids(dev, port, 1); +} + +static int mlx4_ib_netdev_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct net_device *dev = ptr; + struct mlx4_ib_dev *ibdev; + struct net_device *oldnd; + struct mlx4_ib_iboe *iboe; + int port; + + if (!net_eq(dev_net(dev), &init_net)) + return NOTIFY_DONE; + + ibdev = container_of(this, struct mlx4_ib_dev, iboe.nb); + iboe = &ibdev->iboe; + + spin_lock(&iboe->lock); + mlx4_foreach_ib_transport_port(port, ibdev->dev) { + oldnd = iboe->netdevs[port - 1]; + iboe->netdevs[port - 1] = + mlx4_get_protocol_dev(ibdev->dev, MLX4_PROTOCOL_EN, port); + if (oldnd != iboe->netdevs[port - 1]) { + if (iboe->netdevs[port - 1]) + netdev_added(ibdev, port); + else + netdev_removed(ibdev, port); + } + } + + if (dev == iboe->netdevs[0] || + (iboe->netdevs[0] && rdma_vlan_dev_real_dev(dev) == iboe->netdevs[0])) + handle_en_event(ibdev, 1, event); + else if (dev == iboe->netdevs[1] + || (iboe->netdevs[1] && rdma_vlan_dev_real_dev(dev) == iboe->netdevs[1])) + handle_en_event(ibdev, 2, event); + + spin_unlock(&iboe->lock); + + return NOTIFY_DONE; +} + static void *mlx4_ib_add(struct mlx4_dev *dev) { struct mlx4_ib_dev *ibdev; int num_ports = 0; int i; + int err; + struct mlx4_ib_iboe *iboe; printk_once(KERN_INFO "%s", mlx4_ib_version); - mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB) + mlx4_foreach_ib_transport_port(i, dev) num_ports++; /* No point in registering a device with no ports... */ @@ -564,6 +997,8 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) return NULL; } + iboe = &ibdev->iboe; + if (mlx4_pd_alloc(dev, &ibdev->priv_pdn)) goto err_dealloc; @@ -612,6 +1047,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) ibdev->ib_dev.query_device = mlx4_ib_query_device; ibdev->ib_dev.query_port = mlx4_ib_query_port; + ibdev->ib_dev.get_link_layer = mlx4_ib_port_link_layer; ibdev->ib_dev.query_gid = mlx4_ib_query_gid; ibdev->ib_dev.query_pkey = mlx4_ib_query_pkey; ibdev->ib_dev.modify_device = mlx4_ib_modify_device; @@ -656,6 +1092,8 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) ibdev->ib_dev.unmap_fmr = mlx4_ib_unmap_fmr; ibdev->ib_dev.dealloc_fmr = mlx4_ib_fmr_dealloc; + spin_lock_init(&iboe->lock); + if (init_node_data(ibdev)) goto err_map; @@ -668,16 +1106,28 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) if (mlx4_ib_mad_init(ibdev)) goto err_reg; + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_IBOE && !iboe->nb.notifier_call) { + iboe->nb.notifier_call = mlx4_ib_netdev_event; + err = register_netdevice_notifier(&iboe->nb); + if (err) + goto err_reg; + } + for (i = 0; i < ARRAY_SIZE(mlx4_class_attributes); ++i) { if (device_create_file(&ibdev->ib_dev.dev, mlx4_class_attributes[i])) - goto err_reg; + goto err_notif; } ibdev->ib_active = true; return ibdev; +err_notif: + if (unregister_netdevice_notifier(&ibdev->iboe.nb)) + printk(KERN_WARNING "failure unregistering notifier\n"); + flush_workqueue(wq); + err_reg: ib_unregister_device(&ibdev->ib_dev); @@ -703,11 +1153,16 @@ static void mlx4_ib_remove(struct mlx4_dev *dev, void *ibdev_ptr) mlx4_ib_mad_cleanup(ibdev); ib_unregister_device(&ibdev->ib_dev); + if (ibdev->iboe.nb.notifier_call) { + if (unregister_netdevice_notifier(&ibdev->iboe.nb)) + printk(KERN_WARNING "failure unregistering notifier\n"); + ibdev->iboe.nb.notifier_call = NULL; + } + iounmap(ibdev->uar_map); - for (p = 1; p <= ibdev->num_ports; ++p) + mlx4_foreach_port(p, dev, MLX4_PORT_TYPE_IB) mlx4_CLOSE_PORT(dev, p); - iounmap(ibdev->uar_map); mlx4_uar_free(dev, &ibdev->priv_uar); mlx4_pd_free(dev, ibdev->priv_pdn); ib_dealloc_device(&ibdev->ib_dev); @@ -747,19 +1202,33 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr, } static struct mlx4_interface mlx4_ib_interface = { - .add = mlx4_ib_add, - .remove = mlx4_ib_remove, - .event = mlx4_ib_event + .add = mlx4_ib_add, + .remove = mlx4_ib_remove, + .event = mlx4_ib_event, + .protocol = MLX4_PROTOCOL_IB }; static int __init mlx4_ib_init(void) { - return mlx4_register_interface(&mlx4_ib_interface); + int err; + + wq = create_singlethread_workqueue("mlx4_ib"); + if (!wq) + return -ENOMEM; + + err = mlx4_register_interface(&mlx4_ib_interface); + if (err) { + destroy_workqueue(wq); + return err; + } + + return 0; } static void __exit mlx4_ib_cleanup(void) { mlx4_unregister_interface(&mlx4_ib_interface); + destroy_workqueue(wq); } module_init(mlx4_ib_init); diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index 3486d7675e5..2a322f21049 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -112,6 +112,13 @@ enum mlx4_ib_qp_flags { MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK = 1 << 1, }; +struct mlx4_ib_gid_entry { + struct list_head list; + union ib_gid gid; + int added; + u8 port; +}; + struct mlx4_ib_qp { struct ib_qp ibqp; struct mlx4_qp mqp; @@ -138,6 +145,8 @@ struct mlx4_ib_qp { u8 resp_depth; u8 sq_no_prefetch; u8 state; + int mlx_type; + struct list_head gid_list; }; struct mlx4_ib_srq { @@ -157,7 +166,14 @@ struct mlx4_ib_srq { struct mlx4_ib_ah { struct ib_ah ibah; - struct mlx4_av av; + union mlx4_ext_av av; +}; + +struct mlx4_ib_iboe { + spinlock_t lock; + struct net_device *netdevs[MLX4_MAX_PORTS]; + struct notifier_block nb; + union ib_gid gid_table[MLX4_MAX_PORTS][128]; }; struct mlx4_ib_dev { @@ -176,6 +192,7 @@ struct mlx4_ib_dev { struct mutex cap_mask_mutex; bool ib_active; + struct mlx4_ib_iboe iboe; }; static inline struct mlx4_ib_dev *to_mdev(struct ib_device *ibdev) @@ -314,9 +331,20 @@ int mlx4_ib_map_phys_fmr(struct ib_fmr *ibfmr, u64 *page_list, int npages, int mlx4_ib_unmap_fmr(struct list_head *fmr_list); int mlx4_ib_fmr_dealloc(struct ib_fmr *fmr); +int mlx4_ib_resolve_grh(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah_attr, + u8 *mac, int *is_mcast, u8 port); + static inline int mlx4_ib_ah_grh_present(struct mlx4_ib_ah *ah) { - return !!(ah->av.g_slid & 0x80); + u8 port = be32_to_cpu(ah->av.ib.port_pd) >> 24 & 3; + + if (rdma_port_get_link_layer(ah->ibah.device, port) == IB_LINK_LAYER_ETHERNET) + return 1; + + return !!(ah->av.ib.g_slid & 0x80); } +int mlx4_ib_add_mc(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp, + union ib_gid *gid); + #endif /* MLX4_IB_H */ diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 1d27b9a8e2d..dca55b19a6f 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -226,7 +226,7 @@ struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device struct mlx4_ib_fast_reg_page_list *mfrpl; int size = page_list_len * sizeof (u64); - if (size > PAGE_SIZE) + if (page_list_len > MLX4_MAX_FAST_REG_PAGES) return ERR_PTR(-EINVAL); mfrpl = kmalloc(sizeof *mfrpl, GFP_KERNEL); diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 6a60827b230..9a7794ac34c 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -33,9 +33,11 @@ #include <linux/log2.h> #include <linux/slab.h> +#include <linux/netdevice.h> #include <rdma/ib_cache.h> #include <rdma/ib_pack.h> +#include <rdma/ib_addr.h> #include <linux/mlx4/qp.h> @@ -48,17 +50,26 @@ enum { enum { MLX4_IB_DEFAULT_SCHED_QUEUE = 0x83, - MLX4_IB_DEFAULT_QP0_SCHED_QUEUE = 0x3f + MLX4_IB_DEFAULT_QP0_SCHED_QUEUE = 0x3f, + MLX4_IB_LINK_TYPE_IB = 0, + MLX4_IB_LINK_TYPE_ETH = 1 }; enum { /* - * Largest possible UD header: send with GRH and immediate data. + * Largest possible UD header: send with GRH and immediate + * data plus 18 bytes for an Ethernet header with VLAN/802.1Q + * tag. (LRH would only use 8 bytes, so Ethernet is the + * biggest case) */ - MLX4_IB_UD_HEADER_SIZE = 72, + MLX4_IB_UD_HEADER_SIZE = 82, MLX4_IB_LSO_HEADER_SPARE = 128, }; +enum { + MLX4_IB_IBOE_ETHERTYPE = 0x8915 +}; + struct mlx4_ib_sqp { struct mlx4_ib_qp qp; int pkey_index; @@ -462,6 +473,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, mutex_init(&qp->mutex); spin_lock_init(&qp->sq.lock); spin_lock_init(&qp->rq.lock); + INIT_LIST_HEAD(&qp->gid_list); qp->state = IB_QPS_RESET; if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) @@ -649,6 +661,16 @@ static void mlx4_ib_unlock_cqs(struct mlx4_ib_cq *send_cq, struct mlx4_ib_cq *re } } +static void del_gid_entries(struct mlx4_ib_qp *qp) +{ + struct mlx4_ib_gid_entry *ge, *tmp; + + list_for_each_entry_safe(ge, tmp, &qp->gid_list, list) { + list_del(&ge->list); + kfree(ge); + } +} + static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp, int is_user) { @@ -695,6 +717,8 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp, if (!qp->ibqp.srq) mlx4_db_free(dev->dev, &qp->db); } + + del_gid_entries(qp); } struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd, @@ -852,6 +876,14 @@ static void mlx4_set_sched(struct mlx4_qp_path *path, u8 port) static int mlx4_set_path(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah, struct mlx4_qp_path *path, u8 port) { + int err; + int is_eth = rdma_port_get_link_layer(&dev->ib_dev, port) == + IB_LINK_LAYER_ETHERNET; + u8 mac[6]; + int is_mcast; + u16 vlan_tag; + int vidx; + path->grh_mylmc = ah->src_path_bits & 0x7f; path->rlid = cpu_to_be16(ah->dlid); if (ah->static_rate) { @@ -879,12 +911,49 @@ static int mlx4_set_path(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah, memcpy(path->rgid, ah->grh.dgid.raw, 16); } - path->sched_queue = MLX4_IB_DEFAULT_SCHED_QUEUE | - ((port - 1) << 6) | ((ah->sl & 0xf) << 2); + if (is_eth) { + path->sched_queue = MLX4_IB_DEFAULT_SCHED_QUEUE | + ((port - 1) << 6) | ((ah->sl & 7) << 3) | ((ah->sl & 8) >> 1); + + if (!(ah->ah_flags & IB_AH_GRH)) + return -1; + + err = mlx4_ib_resolve_grh(dev, ah, mac, &is_mcast, port); + if (err) + return err; + + memcpy(path->dmac, mac, 6); + path->ackto = MLX4_IB_LINK_TYPE_ETH; + /* use index 0 into MAC table for IBoE */ + path->grh_mylmc &= 0x80; + + vlan_tag = rdma_get_vlan_id(&dev->iboe.gid_table[port - 1][ah->grh.sgid_index]); + if (vlan_tag < 0x1000) { + if (mlx4_find_cached_vlan(dev->dev, port, vlan_tag, &vidx)) + return -ENOENT; + + path->vlan_index = vidx; + path->fl = 1 << 6; + } + } else + path->sched_queue = MLX4_IB_DEFAULT_SCHED_QUEUE | + ((port - 1) << 6) | ((ah->sl & 0xf) << 2); return 0; } +static void update_mcg_macs(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp) +{ + struct mlx4_ib_gid_entry *ge, *tmp; + + list_for_each_entry_safe(ge, tmp, &qp->gid_list, list) { + if (!ge->added && mlx4_ib_add_mc(dev, qp, &ge->gid)) { + ge->added = 1; + ge->port = qp->port; + } + } +} + static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr, int attr_mask, enum ib_qp_state cur_state, enum ib_qp_state new_state) @@ -980,7 +1049,7 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, } if (attr_mask & IB_QP_TIMEOUT) { - context->pri_path.ackto = attr->timeout << 3; + context->pri_path.ackto |= attr->timeout << 3; optpar |= MLX4_QP_OPTPAR_ACK_TIMEOUT; } @@ -1118,8 +1187,10 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, qp->atomic_rd_en = attr->qp_access_flags; if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) qp->resp_depth = attr->max_dest_rd_atomic; - if (attr_mask & IB_QP_PORT) + if (attr_mask & IB_QP_PORT) { qp->port = attr->port_num; + update_mcg_macs(dev, qp); + } if (attr_mask & IB_QP_ALT_PATH) qp->alt_port = attr->alt_port_num; @@ -1221,40 +1292,59 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr, struct mlx4_wqe_mlx_seg *mlx = wqe; struct mlx4_wqe_inline_seg *inl = wqe + sizeof *mlx; struct mlx4_ib_ah *ah = to_mah(wr->wr.ud.ah); + union ib_gid sgid; u16 pkey; int send_size; int header_size; int spc; int i; + int is_eth; + int is_vlan = 0; + int is_grh; + u16 vlan; send_size = 0; for (i = 0; i < wr->num_sge; ++i) send_size += wr->sg_list[i].length; - ib_ud_header_init(send_size, mlx4_ib_ah_grh_present(ah), 0, &sqp->ud_header); + is_eth = rdma_port_get_link_layer(sqp->qp.ibqp.device, sqp->qp.port) == IB_LINK_LAYER_ETHERNET; + is_grh = mlx4_ib_ah_grh_present(ah); + if (is_eth) { + ib_get_cached_gid(ib_dev, be32_to_cpu(ah->av.ib.port_pd) >> 24, + ah->av.ib.gid_index, &sgid); + vlan = rdma_get_vlan_id(&sgid); + is_vlan = vlan < 0x1000; + } + ib_ud_header_init(send_size, !is_eth, is_eth, is_vlan, is_grh, 0, &sqp->ud_header); + + if (!is_eth) { + sqp->ud_header.lrh.service_level = + be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 28; + sqp->ud_header.lrh.destination_lid = ah->av.ib.dlid; + sqp->ud_header.lrh.source_lid = cpu_to_be16(ah->av.ib.g_slid & 0x7f); + } - sqp->ud_header.lrh.service_level = - be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 28; - sqp->ud_header.lrh.destination_lid = ah->av.dlid; - sqp->ud_header.lrh.source_lid = cpu_to_be16(ah->av.g_slid & 0x7f); - if (mlx4_ib_ah_grh_present(ah)) { + if (is_grh) { sqp->ud_header.grh.traffic_class = - (be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 20) & 0xff; + (be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 20) & 0xff; sqp->ud_header.grh.flow_label = - ah->av.sl_tclass_flowlabel & cpu_to_be32(0xfffff); - sqp->ud_header.grh.hop_limit = ah->av.hop_limit; - ib_get_cached_gid(ib_dev, be32_to_cpu(ah->av.port_pd) >> 24, - ah->av.gid_index, &sqp->ud_header.grh.source_gid); + ah->av.ib.sl_tclass_flowlabel & cpu_to_be32(0xfffff); + sqp->ud_header.grh.hop_limit = ah->av.ib.hop_limit; + ib_get_cached_gid(ib_dev, be32_to_cpu(ah->av.ib.port_pd) >> 24, + ah->av.ib.gid_index, &sqp->ud_header.grh.source_gid); memcpy(sqp->ud_header.grh.destination_gid.raw, - ah->av.dgid, 16); + ah->av.ib.dgid, 16); } mlx->flags &= cpu_to_be32(MLX4_WQE_CTRL_CQ_UPDATE); - mlx->flags |= cpu_to_be32((!sqp->qp.ibqp.qp_num ? MLX4_WQE_MLX_VL15 : 0) | - (sqp->ud_header.lrh.destination_lid == - IB_LID_PERMISSIVE ? MLX4_WQE_MLX_SLR : 0) | - (sqp->ud_header.lrh.service_level << 8)); - mlx->rlid = sqp->ud_header.lrh.destination_lid; + + if (!is_eth) { + mlx->flags |= cpu_to_be32((!sqp->qp.ibqp.qp_num ? MLX4_WQE_MLX_VL15 : 0) | + (sqp->ud_header.lrh.destination_lid == + IB_LID_PERMISSIVE ? MLX4_WQE_MLX_SLR : 0) | + (sqp->ud_header.lrh.service_level << 8)); + mlx->rlid = sqp->ud_header.lrh.destination_lid; + } switch (wr->opcode) { case IB_WR_SEND: @@ -1270,9 +1360,29 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr, return -EINVAL; } - sqp->ud_header.lrh.virtual_lane = !sqp->qp.ibqp.qp_num ? 15 : 0; - if (sqp->ud_header.lrh.destination_lid == IB_LID_PERMISSIVE) - sqp->ud_header.lrh.source_lid = IB_LID_PERMISSIVE; + if (is_eth) { + u8 *smac; + + memcpy(sqp->ud_header.eth.dmac_h, ah->av.eth.mac, 6); + /* FIXME: cache smac value? */ + smac = to_mdev(sqp->qp.ibqp.device)->iboe.netdevs[sqp->qp.port - 1]->dev_addr; + memcpy(sqp->ud_header.eth.smac_h, smac, 6); + if (!memcmp(sqp->ud_header.eth.smac_h, sqp->ud_header.eth.dmac_h, 6)) + mlx->flags |= cpu_to_be32(MLX4_WQE_CTRL_FORCE_LOOPBACK); + if (!is_vlan) { + sqp->ud_header.eth.type = cpu_to_be16(MLX4_IB_IBOE_ETHERTYPE); + } else { + u16 pcp; + + sqp->ud_header.vlan.type = cpu_to_be16(MLX4_IB_IBOE_ETHERTYPE); + pcp = (be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 27 & 3) << 13; + sqp->ud_header.vlan.tag = cpu_to_be16(vlan | pcp); + } + } else { + sqp->ud_header.lrh.virtual_lane = !sqp->qp.ibqp.qp_num ? 15 : 0; + if (sqp->ud_header.lrh.destination_lid == IB_LID_PERMISSIVE) + sqp->ud_header.lrh.source_lid = IB_LID_PERMISSIVE; + } sqp->ud_header.bth.solicited_event = !!(wr->send_flags & IB_SEND_SOLICITED); if (!sqp->qp.ibqp.qp_num) ib_get_cached_pkey(ib_dev, sqp->qp.port, sqp->pkey_index, &pkey); @@ -1429,11 +1539,14 @@ static void set_masked_atomic_seg(struct mlx4_wqe_masked_atomic_seg *aseg, } static void set_datagram_seg(struct mlx4_wqe_datagram_seg *dseg, - struct ib_send_wr *wr) + struct ib_send_wr *wr, __be16 *vlan) { memcpy(dseg->av, &to_mah(wr->wr.ud.ah)->av, sizeof (struct mlx4_av)); dseg->dqpn = cpu_to_be32(wr->wr.ud.remote_qpn); dseg->qkey = cpu_to_be32(wr->wr.ud.remote_qkey); + dseg->vlan = to_mah(wr->wr.ud.ah)->av.eth.vlan; + memcpy(dseg->mac, to_mah(wr->wr.ud.ah)->av.eth.mac, 6); + *vlan = dseg->vlan; } static void set_mlx_icrc_seg(void *dseg) @@ -1536,6 +1649,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, __be32 uninitialized_var(lso_hdr_sz); __be32 blh; int i; + __be16 vlan = cpu_to_be16(0xffff); spin_lock_irqsave(&qp->sq.lock, flags); @@ -1639,7 +1753,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, break; case IB_QPT_UD: - set_datagram_seg(wqe, wr); + set_datagram_seg(wqe, wr, &vlan); wqe += sizeof (struct mlx4_wqe_datagram_seg); size += sizeof (struct mlx4_wqe_datagram_seg) / 16; @@ -1717,6 +1831,11 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, ctrl->owner_opcode = mlx4_ib_opcode[wr->opcode] | (ind & qp->sq.wqe_cnt ? cpu_to_be32(1 << 31) : 0) | blh; + if (be16_to_cpu(vlan) < 0x1000) { + ctrl->ins_vlan = 1 << 6; + ctrl->vlan_tag = vlan; + } + stamp = ind + qp->sq_spare_wqes; ind += DIV_ROUND_UP(size * 16, 1U << qp->sq.wqe_shift); @@ -1866,17 +1985,27 @@ static int to_ib_qp_access_flags(int mlx4_flags) return ib_flags; } -static void to_ib_ah_attr(struct mlx4_dev *dev, struct ib_ah_attr *ib_ah_attr, +static void to_ib_ah_attr(struct mlx4_ib_dev *ibdev, struct ib_ah_attr *ib_ah_attr, struct mlx4_qp_path *path) { + struct mlx4_dev *dev = ibdev->dev; + int is_eth; + memset(ib_ah_attr, 0, sizeof *ib_ah_attr); ib_ah_attr->port_num = path->sched_queue & 0x40 ? 2 : 1; if (ib_ah_attr->port_num == 0 || ib_ah_attr->port_num > dev->caps.num_ports) return; + is_eth = rdma_port_get_link_layer(&ibdev->ib_dev, ib_ah_attr->port_num) == + IB_LINK_LAYER_ETHERNET; + if (is_eth) + ib_ah_attr->sl = ((path->sched_queue >> 3) & 0x7) | + ((path->sched_queue & 4) << 1); + else + ib_ah_attr->sl = (path->sched_queue >> 2) & 0xf; + ib_ah_attr->dlid = be16_to_cpu(path->rlid); - ib_ah_attr->sl = (path->sched_queue >> 2) & 0xf; ib_ah_attr->src_path_bits = path->grh_mylmc & 0x7f; ib_ah_attr->static_rate = path->static_rate ? path->static_rate - 5 : 0; ib_ah_attr->ah_flags = (path->grh_mylmc & (1 << 7)) ? IB_AH_GRH : 0; @@ -1929,8 +2058,8 @@ int mlx4_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr to_ib_qp_access_flags(be32_to_cpu(context.params2)); if (qp->ibqp.qp_type == IB_QPT_RC || qp->ibqp.qp_type == IB_QPT_UC) { - to_ib_ah_attr(dev->dev, &qp_attr->ah_attr, &context.pri_path); - to_ib_ah_attr(dev->dev, &qp_attr->alt_ah_attr, &context.alt_path); + to_ib_ah_attr(dev, &qp_attr->ah_attr, &context.pri_path); + to_ib_ah_attr(dev, &qp_attr->alt_ah_attr, &context.alt_path); qp_attr->alt_pkey_index = context.alt_path.pkey_index & 0x7f; qp_attr->alt_port_num = qp_attr->alt_ah_attr.port_num; } diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index d2d172e6289..a34c9d38e82 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c @@ -1493,7 +1493,7 @@ static int build_mlx_header(struct mthca_dev *dev, struct mthca_sqp *sqp, int err; u16 pkey; - ib_ud_header_init(256, /* assume a MAD */ + ib_ud_header_init(256, /* assume a MAD */ 1, 0, 0, mthca_ah_grh_present(to_mah(wr->wr.ud.ah)), 0, &sqp->ud_header); diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 6220d9d75b5..25ad0f9944c 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -1424,7 +1424,6 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb, { int reset = 0; /* whether to send reset in case of err.. */ - int passive_state; atomic_inc(&cm_resets_recvd); nes_debug(NES_DBG_CM, "Received Reset, cm_node = %p, state = %u." " refcnt=%d\n", cm_node, cm_node->state, @@ -1439,7 +1438,7 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb, active_open_err(cm_node, skb, reset); break; case NES_CM_STATE_MPAREQ_RCVD: - passive_state = atomic_add_return(1, &cm_node->passive_state); + atomic_inc(&cm_node->passive_state); dev_kfree_skb_any(skb); break; case NES_CM_STATE_ESTABLISHED: diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index 10560c796fd..3892e2c0e95 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -271,6 +271,7 @@ static int nes_netdev_stop(struct net_device *netdev) if (netif_msg_ifdown(nesvnic)) printk(KERN_INFO PFX "%s: disabling interface\n", netdev->name); + netif_carrier_off(netdev); /* Disable network packets */ napi_disable(&nesvnic->napi); diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 546fc22405f..99933e4e48f 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -476,9 +476,9 @@ static struct ib_fast_reg_page_list *nes_alloc_fast_reg_page_list( } nes_debug(NES_DBG_MR, "nes_alloc_fast_reg_pbl: nes_frpl = %p, " "ibfrpl = %p, ibfrpl.page_list = %p, pbl.kva = %p, " - "pbl.paddr= %p\n", pnesfrpl, &pnesfrpl->ibfrpl, + "pbl.paddr = %llx\n", pnesfrpl, &pnesfrpl->ibfrpl, pnesfrpl->ibfrpl.page_list, pnesfrpl->nes_wqe_pbl.kva, - (void *)pnesfrpl->nes_wqe_pbl.paddr); + (unsigned long long) pnesfrpl->nes_wqe_pbl.paddr); return pifrpl; } @@ -584,7 +584,9 @@ static int nes_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr props->lmc = 0; props->sm_lid = 0; props->sm_sl = 0; - if (nesvnic->linkup) + if (netif_queue_stopped(netdev)) + props->state = IB_PORT_DOWN; + else if (nesvnic->linkup) props->state = IB_PORT_ACTIVE; else props->state = IB_PORT_DOWN; @@ -3483,13 +3485,13 @@ static int nes_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr, for (i = 0; i < ib_wr->wr.fast_reg.page_list_len; i++) dst_page_list[i] = cpu_to_le64(src_page_list[i]); - nes_debug(NES_DBG_IW_TX, "SQ_FMR: iova_start: %p, " - "length: %d, rkey: %0x, pgl_paddr: %p, " + nes_debug(NES_DBG_IW_TX, "SQ_FMR: iova_start: %llx, " + "length: %d, rkey: %0x, pgl_paddr: %llx, " "page_list_len: %u, wqe_misc: %x\n", - (void *)ib_wr->wr.fast_reg.iova_start, + (unsigned long long) ib_wr->wr.fast_reg.iova_start, ib_wr->wr.fast_reg.length, ib_wr->wr.fast_reg.rkey, - (void *)pnesfrpl->nes_wqe_pbl.paddr, + (unsigned long long) pnesfrpl->nes_wqe_pbl.paddr, ib_wr->wr.fast_reg.page_list_len, wqe_misc); break; diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h index 61de0654820..64c9e7d02d4 100644 --- a/drivers/infiniband/hw/qib/qib.h +++ b/drivers/infiniband/hw/qib/qib.h @@ -1406,7 +1406,7 @@ extern struct mutex qib_mutex; */ #define qib_early_err(dev, fmt, ...) \ do { \ - dev_info(dev, KERN_ERR QIB_DRV_NAME ": " fmt, ##__VA_ARGS__); \ + dev_err(dev, fmt, ##__VA_ARGS__); \ } while (0) #define qib_dev_err(dd, fmt, ...) \ diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c index f1d16d3a01f..f3b50393604 100644 --- a/drivers/infiniband/hw/qib/qib_init.c +++ b/drivers/infiniband/hw/qib/qib_init.c @@ -1243,6 +1243,7 @@ static int __devinit qib_init_one(struct pci_dev *pdev, qib_early_err(&pdev->dev, "QLogic PCIE device 0x%x cannot " "work if CONFIG_PCI_MSI is not enabled\n", ent->device); + dd = ERR_PTR(-ENODEV); #endif break; diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c index 7fa6e559263..48b6674cbc4 100644 --- a/drivers/infiniband/hw/qib/qib_pcie.c +++ b/drivers/infiniband/hw/qib/qib_pcie.c @@ -103,16 +103,20 @@ int qib_pcie_init(struct pci_dev *pdev, const struct pci_device_id *ent) ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); } else ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); - if (ret) + if (ret) { qib_early_err(&pdev->dev, "Unable to set DMA consistent mask: %d\n", ret); + goto bail; + } pci_set_master(pdev); ret = pci_enable_pcie_error_reporting(pdev); - if (ret) + if (ret) { qib_early_err(&pdev->dev, "Unable to enable pcie error reporting: %d\n", ret); + ret = 0; + } goto done; bail: diff --git a/drivers/infiniband/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c index a0931119bd7..955fb715779 100644 --- a/drivers/infiniband/hw/qib/qib_rc.c +++ b/drivers/infiniband/hw/qib/qib_rc.c @@ -2068,7 +2068,10 @@ send_last: goto nack_op_err; if (!ret) goto rnr_nak; - goto send_last_imm; + wc.ex.imm_data = ohdr->u.rc.imm_data; + hdrsize += 4; + wc.wc_flags = IB_WC_WITH_IMM; + goto send_last; case OP(RDMA_READ_REQUEST): { struct qib_ack_entry *e; diff --git a/drivers/infiniband/hw/qib/qib_uc.c b/drivers/infiniband/hw/qib/qib_uc.c index b9c8b6346c1..32ccf3c824c 100644 --- a/drivers/infiniband/hw/qib/qib_uc.c +++ b/drivers/infiniband/hw/qib/qib_uc.c @@ -457,8 +457,10 @@ rdma_first: } if (opcode == OP(RDMA_WRITE_ONLY)) goto rdma_last; - else if (opcode == OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE)) + else if (opcode == OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE)) { + wc.ex.imm_data = ohdr->u.rc.imm_data; goto rdma_last_imm; + } /* FALLTHROUGH */ case OP(RDMA_WRITE_MIDDLE): /* Check for invalid length PMTU or posted rwqe len. */ @@ -471,8 +473,8 @@ rdma_first: break; case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE): -rdma_last_imm: wc.ex.imm_data = ohdr->u.imm_data; +rdma_last_imm: hdrsize += 4; wc.wc_flags = IB_WC_WITH_IMM; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index ec6b4fbe25e..dfa71903d6e 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -223,6 +223,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) unsigned int wr_id = wc->wr_id & ~IPOIB_OP_RECV; struct sk_buff *skb; u64 mapping[IPOIB_UD_RX_SG]; + union ib_gid *dgid; ipoib_dbg_data(priv, "recv completion: id %d, status: %d\n", wr_id, wc->status); @@ -271,6 +272,16 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) ipoib_ud_dma_unmap_rx(priv, mapping); ipoib_ud_skb_put_frags(priv, skb, wc->byte_len); + /* First byte of dgid signals multicast when 0xff */ + dgid = &((struct ib_grh *)skb->data)->dgid; + + if (!(wc->wc_flags & IB_WC_GRH) || dgid->raw[0] != 0xff) + skb->pkt_type = PACKET_HOST; + else if (memcmp(dgid, dev->broadcast + 4, sizeof(union ib_gid)) == 0) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + skb_pull(skb, IB_GRH_BYTES); skb->protocol = ((struct ipoib_header *) skb->data)->proto; @@ -281,9 +292,6 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) dev->stats.rx_bytes += skb->len; skb->dev = dev; - /* XXX get correct PACKET_ type here */ - skb->pkt_type = PACKET_HOST; - if (test_bit(IPOIB_FLAG_CSUM, &priv->flags) && likely(wc->csum_ok)) skb->ip_summed = CHECKSUM_UNNECESSARY; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index b4b22576f12..9ff7bc73ed9 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1240,6 +1240,7 @@ static struct net_device *ipoib_add_port(const char *format, goto alloc_mem_failed; SET_NETDEV_DEV(priv->dev, hca->dma_device); + priv->dev->dev_id = port - 1; if (!ib_query_port(hca, port, &attr)) priv->max_ib_mtu = ib_mtu_enum_to_int(attr.max_mtu); @@ -1362,6 +1363,8 @@ static void ipoib_add_one(struct ib_device *device) } for (p = s; p <= e; ++p) { + if (rdma_port_get_link_layer(device, p) != IB_LINK_LAYER_INFINIBAND) + continue; dev = ipoib_add_port("ib%d", device, p); if (!IS_ERR(dev)) { priv = netdev_priv(dev); @@ -1409,8 +1412,7 @@ static int __init ipoib_init_module(void) ipoib_sendq_size = roundup_pow_of_two(ipoib_sendq_size); ipoib_sendq_size = min(ipoib_sendq_size, IPOIB_MAX_QUEUE_SIZE); - ipoib_sendq_size = max(ipoib_sendq_size, max(2 * MAX_SEND_CQE, - IPOIB_MIN_QUEUE_SIZE)); + ipoib_sendq_size = max3(ipoib_sendq_size, 2 * MAX_SEND_CQE, IPOIB_MIN_QUEUE_SIZE); #ifdef CONFIG_INFINIBAND_IPOIB_CM ipoib_max_conn_qp = min(ipoib_max_conn_qp, IPOIB_CM_MAX_CONN_QP); #endif diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 7f8f16bad75..cfc1d65c457 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -291,7 +291,7 @@ static void srp_free_target_ib(struct srp_target_port *target) for (i = 0; i < SRP_RQ_SIZE; ++i) srp_free_iu(target->srp_host, target->rx_ring[i]); - for (i = 0; i < SRP_SQ_SIZE + 1; ++i) + for (i = 0; i < SRP_SQ_SIZE; ++i) srp_free_iu(target->srp_host, target->tx_ring[i]); } @@ -811,6 +811,75 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target, return len; } +/* + * Must be called with target->scsi_host->host_lock held to protect + * req_lim and tx_head. Lock cannot be dropped between call here and + * call to __srp_post_send(). + * + * Note: + * An upper limit for the number of allocated information units for each + * request type is: + * - SRP_IU_CMD: SRP_CMD_SQ_SIZE, since the SCSI mid-layer never queues + * more than Scsi_Host.can_queue requests. + * - SRP_IU_TSK_MGMT: SRP_TSK_MGMT_SQ_SIZE. + * - SRP_IU_RSP: 1, since a conforming SRP target never sends more than + * one unanswered SRP request to an initiator. + */ +static struct srp_iu *__srp_get_tx_iu(struct srp_target_port *target, + enum srp_iu_type iu_type) +{ + s32 rsv = (iu_type == SRP_IU_TSK_MGMT) ? 0 : SRP_TSK_MGMT_SQ_SIZE; + struct srp_iu *iu; + + srp_send_completion(target->send_cq, target); + + if (target->tx_head - target->tx_tail >= SRP_SQ_SIZE) + return NULL; + + /* Initiator responses to target requests do not consume credits */ + if (target->req_lim <= rsv && iu_type != SRP_IU_RSP) { + ++target->zero_req_lim; + return NULL; + } + + iu = target->tx_ring[target->tx_head & SRP_SQ_MASK]; + iu->type = iu_type; + return iu; +} + +/* + * Must be called with target->scsi_host->host_lock held to protect + * req_lim and tx_head. + */ +static int __srp_post_send(struct srp_target_port *target, + struct srp_iu *iu, int len) +{ + struct ib_sge list; + struct ib_send_wr wr, *bad_wr; + int ret = 0; + + list.addr = iu->dma; + list.length = len; + list.lkey = target->srp_host->srp_dev->mr->lkey; + + wr.next = NULL; + wr.wr_id = target->tx_head & SRP_SQ_MASK; + wr.sg_list = &list; + wr.num_sge = 1; + wr.opcode = IB_WR_SEND; + wr.send_flags = IB_SEND_SIGNALED; + + ret = ib_post_send(target->qp, &wr, &bad_wr); + + if (!ret) { + ++target->tx_head; + if (iu->type != SRP_IU_RSP) + --target->req_lim; + } + + return ret; +} + static int srp_post_recv(struct srp_target_port *target) { unsigned long flags; @@ -822,7 +891,7 @@ static int srp_post_recv(struct srp_target_port *target) spin_lock_irqsave(target->scsi_host->host_lock, flags); - next = target->rx_head & (SRP_RQ_SIZE - 1); + next = target->rx_head & SRP_RQ_MASK; wr.wr_id = next; iu = target->rx_ring[next]; @@ -896,6 +965,71 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp) spin_unlock_irqrestore(target->scsi_host->host_lock, flags); } +static int srp_response_common(struct srp_target_port *target, s32 req_delta, + void *rsp, int len) +{ + struct ib_device *dev; + unsigned long flags; + struct srp_iu *iu; + int err = 1; + + dev = target->srp_host->srp_dev->dev; + + spin_lock_irqsave(target->scsi_host->host_lock, flags); + target->req_lim += req_delta; + + iu = __srp_get_tx_iu(target, SRP_IU_RSP); + if (!iu) { + shost_printk(KERN_ERR, target->scsi_host, PFX + "no IU available to send response\n"); + goto out; + } + + ib_dma_sync_single_for_cpu(dev, iu->dma, len, DMA_TO_DEVICE); + memcpy(iu->buf, rsp, len); + ib_dma_sync_single_for_device(dev, iu->dma, len, DMA_TO_DEVICE); + + err = __srp_post_send(target, iu, len); + if (err) + shost_printk(KERN_ERR, target->scsi_host, PFX + "unable to post response: %d\n", err); + +out: + spin_unlock_irqrestore(target->scsi_host->host_lock, flags); + return err; +} + +static void srp_process_cred_req(struct srp_target_port *target, + struct srp_cred_req *req) +{ + struct srp_cred_rsp rsp = { + .opcode = SRP_CRED_RSP, + .tag = req->tag, + }; + s32 delta = be32_to_cpu(req->req_lim_delta); + + if (srp_response_common(target, delta, &rsp, sizeof rsp)) + shost_printk(KERN_ERR, target->scsi_host, PFX + "problems processing SRP_CRED_REQ\n"); +} + +static void srp_process_aer_req(struct srp_target_port *target, + struct srp_aer_req *req) +{ + struct srp_aer_rsp rsp = { + .opcode = SRP_AER_RSP, + .tag = req->tag, + }; + s32 delta = be32_to_cpu(req->req_lim_delta); + + shost_printk(KERN_ERR, target->scsi_host, PFX + "ignoring AER for LUN %llu\n", be64_to_cpu(req->lun)); + + if (srp_response_common(target, delta, &rsp, sizeof rsp)) + shost_printk(KERN_ERR, target->scsi_host, PFX + "problems processing SRP_AER_REQ\n"); +} + static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc) { struct ib_device *dev; @@ -923,6 +1057,14 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc) srp_process_rsp(target, iu->buf); break; + case SRP_CRED_REQ: + srp_process_cred_req(target, iu->buf); + break; + + case SRP_AER_REQ: + srp_process_aer_req(target, iu->buf); + break; + case SRP_T_LOGOUT: /* XXX Handle target logout */ shost_printk(KERN_WARNING, target->scsi_host, @@ -981,61 +1123,6 @@ static void srp_send_completion(struct ib_cq *cq, void *target_ptr) } } -/* - * Must be called with target->scsi_host->host_lock held to protect - * req_lim and tx_head. Lock cannot be dropped between call here and - * call to __srp_post_send(). - */ -static struct srp_iu *__srp_get_tx_iu(struct srp_target_port *target, - enum srp_request_type req_type) -{ - s32 min = (req_type == SRP_REQ_TASK_MGMT) ? 1 : 2; - - srp_send_completion(target->send_cq, target); - - if (target->tx_head - target->tx_tail >= SRP_SQ_SIZE) - return NULL; - - if (target->req_lim < min) { - ++target->zero_req_lim; - return NULL; - } - - return target->tx_ring[target->tx_head & SRP_SQ_SIZE]; -} - -/* - * Must be called with target->scsi_host->host_lock held to protect - * req_lim and tx_head. - */ -static int __srp_post_send(struct srp_target_port *target, - struct srp_iu *iu, int len) -{ - struct ib_sge list; - struct ib_send_wr wr, *bad_wr; - int ret = 0; - - list.addr = iu->dma; - list.length = len; - list.lkey = target->srp_host->srp_dev->mr->lkey; - - wr.next = NULL; - wr.wr_id = target->tx_head & SRP_SQ_SIZE; - wr.sg_list = &list; - wr.num_sge = 1; - wr.opcode = IB_WR_SEND; - wr.send_flags = IB_SEND_SIGNALED; - - ret = ib_post_send(target->qp, &wr, &bad_wr); - - if (!ret) { - ++target->tx_head; - --target->req_lim; - } - - return ret; -} - static int srp_queuecommand(struct scsi_cmnd *scmnd, void (*done)(struct scsi_cmnd *)) { @@ -1056,7 +1143,7 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd, return 0; } - iu = __srp_get_tx_iu(target, SRP_REQ_NORMAL); + iu = __srp_get_tx_iu(target, SRP_IU_CMD); if (!iu) goto err; @@ -1064,7 +1151,7 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd, ib_dma_sync_single_for_cpu(dev, iu->dma, srp_max_iu_len, DMA_TO_DEVICE); - req = list_entry(target->free_reqs.next, struct srp_request, list); + req = list_first_entry(&target->free_reqs, struct srp_request, list); scmnd->scsi_done = done; scmnd->result = 0; @@ -1121,7 +1208,7 @@ static int srp_alloc_iu_bufs(struct srp_target_port *target) goto err; } - for (i = 0; i < SRP_SQ_SIZE + 1; ++i) { + for (i = 0; i < SRP_SQ_SIZE; ++i) { target->tx_ring[i] = srp_alloc_iu(target->srp_host, srp_max_iu_len, GFP_KERNEL, DMA_TO_DEVICE); @@ -1137,7 +1224,7 @@ err: target->rx_ring[i] = NULL; } - for (i = 0; i < SRP_SQ_SIZE + 1; ++i) { + for (i = 0; i < SRP_SQ_SIZE; ++i) { srp_free_iu(target->srp_host, target->tx_ring[i]); target->tx_ring[i] = NULL; } @@ -1252,8 +1339,13 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) target->max_ti_iu_len = be32_to_cpu(rsp->max_ti_iu_len); target->req_lim = be32_to_cpu(rsp->req_lim_delta); - target->scsi_host->can_queue = min(target->req_lim, - target->scsi_host->can_queue); + /* + * Reserve credits for task management so we don't + * bounce requests back to the SCSI mid-layer. + */ + target->scsi_host->can_queue + = min(target->req_lim - SRP_TSK_MGMT_SQ_SIZE, + target->scsi_host->can_queue); } else { shost_printk(KERN_WARNING, target->scsi_host, PFX "Unhandled RSP opcode %#x\n", opcode); @@ -1350,6 +1442,7 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) static int srp_send_tsk_mgmt(struct srp_target_port *target, struct srp_request *req, u8 func) { + struct ib_device *dev = target->srp_host->srp_dev->dev; struct srp_iu *iu; struct srp_tsk_mgmt *tsk_mgmt; @@ -1363,10 +1456,12 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target, init_completion(&req->done); - iu = __srp_get_tx_iu(target, SRP_REQ_TASK_MGMT); + iu = __srp_get_tx_iu(target, SRP_IU_TSK_MGMT); if (!iu) goto out; + ib_dma_sync_single_for_cpu(dev, iu->dma, sizeof *tsk_mgmt, + DMA_TO_DEVICE); tsk_mgmt = iu->buf; memset(tsk_mgmt, 0, sizeof *tsk_mgmt); @@ -1376,6 +1471,8 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target, tsk_mgmt->tsk_mgmt_func = func; tsk_mgmt->task_tag = req->index; + ib_dma_sync_single_for_device(dev, iu->dma, sizeof *tsk_mgmt, + DMA_TO_DEVICE); if (__srp_post_send(target, iu, sizeof *tsk_mgmt)) goto out; @@ -1626,9 +1723,9 @@ static struct scsi_host_template srp_template = { .eh_abort_handler = srp_abort, .eh_device_reset_handler = srp_reset_device, .eh_host_reset_handler = srp_reset_host, - .can_queue = SRP_SQ_SIZE, + .can_queue = SRP_CMD_SQ_SIZE, .this_id = -1, - .cmd_per_lun = SRP_SQ_SIZE, + .cmd_per_lun = SRP_CMD_SQ_SIZE, .use_clustering = ENABLE_CLUSTERING, .shost_attrs = srp_host_attrs }; @@ -1813,7 +1910,7 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target) printk(KERN_WARNING PFX "bad max cmd_per_lun parameter '%s'\n", p); goto out; } - target->scsi_host->cmd_per_lun = min(token, SRP_SQ_SIZE); + target->scsi_host->cmd_per_lun = min(token, SRP_CMD_SQ_SIZE); break; case SRP_OPT_IO_CLASS: @@ -1891,7 +1988,7 @@ static ssize_t srp_create_target(struct device *dev, INIT_LIST_HEAD(&target->free_reqs); INIT_LIST_HEAD(&target->req_queue); - for (i = 0; i < SRP_SQ_SIZE; ++i) { + for (i = 0; i < SRP_CMD_SQ_SIZE; ++i) { target->req_ring[i].index = i; list_add_tail(&target->req_ring[i].list, &target->free_reqs); } @@ -2159,6 +2256,9 @@ static int __init srp_init_module(void) { int ret; + BUILD_BUG_ON_NOT_POWER_OF_2(SRP_SQ_SIZE); + BUILD_BUG_ON_NOT_POWER_OF_2(SRP_RQ_SIZE); + if (srp_sg_tablesize > 255) { printk(KERN_WARNING PFX "Clamping srp_sg_tablesize to 255\n"); srp_sg_tablesize = 255; diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h index 5a80eac6fda..ed0dce9e479 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.h +++ b/drivers/infiniband/ulp/srp/ib_srp.h @@ -59,7 +59,14 @@ enum { SRP_RQ_SHIFT = 6, SRP_RQ_SIZE = 1 << SRP_RQ_SHIFT, - SRP_SQ_SIZE = SRP_RQ_SIZE - 1, + SRP_RQ_MASK = SRP_RQ_SIZE - 1, + + SRP_SQ_SIZE = SRP_RQ_SIZE, + SRP_SQ_MASK = SRP_SQ_SIZE - 1, + SRP_RSP_SQ_SIZE = 1, + SRP_REQ_SQ_SIZE = SRP_SQ_SIZE - SRP_RSP_SQ_SIZE, + SRP_TSK_MGMT_SQ_SIZE = 1, + SRP_CMD_SQ_SIZE = SRP_REQ_SQ_SIZE - SRP_TSK_MGMT_SQ_SIZE, SRP_TAG_TSK_MGMT = 1 << (SRP_RQ_SHIFT + 1), @@ -75,9 +82,10 @@ enum srp_target_state { SRP_TARGET_REMOVED }; -enum srp_request_type { - SRP_REQ_NORMAL, - SRP_REQ_TASK_MGMT, +enum srp_iu_type { + SRP_IU_CMD, + SRP_IU_TSK_MGMT, + SRP_IU_RSP, }; struct srp_device { @@ -144,11 +152,11 @@ struct srp_target_port { unsigned tx_head; unsigned tx_tail; - struct srp_iu *tx_ring[SRP_SQ_SIZE + 1]; + struct srp_iu *tx_ring[SRP_SQ_SIZE]; struct list_head free_reqs; struct list_head req_queue; - struct srp_request req_ring[SRP_SQ_SIZE]; + struct srp_request req_ring[SRP_CMD_SQ_SIZE]; struct work_struct work; @@ -164,6 +172,7 @@ struct srp_iu { void *buf; size_t size; enum dma_data_direction direction; + enum srp_iu_type type; }; #endif /* IB_SRP_H */ diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c index 947d4afa25c..30e6195e19d 100644 --- a/drivers/macintosh/windfarm_pm121.c +++ b/drivers/macintosh/windfarm_pm121.c @@ -482,7 +482,7 @@ static s32 pm121_correct(s32 new_setpoint, new_min += correction->offset; new_min = (new_min >> 16) + min; - return max(new_setpoint, max(new_min, 0)); + return max3(new_setpoint, new_min, 0); } static s32 pm121_connect(unsigned int control_id, s32 setpoint) diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 0b61792a278..2129cdb115d 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -254,7 +254,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw, * Issue the synchronous I/O from a different thread * to avoid generic_make_request recursion. */ - INIT_WORK_ON_STACK(&req.work, do_metadata); + INIT_WORK_ONSTACK(&req.work, do_metadata); queue_work(ps->metadata_wq, &req.work); flush_workqueue(ps->metadata_wq); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 1f69743b12e..5a74db75f66 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -4,7 +4,6 @@ menuconfig MISC_DEVICES bool "Misc devices" - default y ---help--- Say Y here to get to see options for device drivers from various different categories. This option alone does not add any kernel code. @@ -24,7 +23,8 @@ config AD525X_DPOT AD5260, AD5262, AD5263, AD5290, AD5291, AD5292, AD5293, AD7376, AD8400, AD8402, AD8403, ADN2850, AD5241, AD5242, AD5243, AD5245, AD5246, AD5247, AD5248, AD5280, AD5282, - ADN2860, AD5273, AD5171, AD5170, AD5172, AD5173 + ADN2860, AD5273, AD5171, AD5170, AD5172, AD5173, AD5270, + AD5271, AD5272, AD5274 digital potentiometer chips. See Documentation/misc-devices/ad525x_dpot.txt for the @@ -284,6 +284,16 @@ config SGI_GRU_DEBUG This option enables addition debugging code for the SGI GRU driver. If you are unsure, say N. +config APDS9802ALS + tristate "Medfield Avago APDS9802 ALS Sensor module" + depends on I2C + help + If you say yes here you get support for the ALS APDS9802 ambient + light sensor. + + This driver can also be built as a module. If so, the module + will be called apds9802als. + config ISL29003 tristate "Intersil ISL29003 ambient light sensor" depends on I2C && SYSFS @@ -294,6 +304,16 @@ config ISL29003 This driver can also be built as a module. If so, the module will be called isl29003. +config ISL29020 + tristate "Intersil ISL29020 ambient light sensor" + depends on I2C + help + If you say yes here you get support for the Intersil ISL29020 + ambient light sensor. + + This driver can also be built as a module. If so, the module + will be called isl29020. + config SENSORS_TSL2550 tristate "Taos TSL2550 ambient light sensor" depends on I2C && SYSFS @@ -314,6 +334,27 @@ config SENSORS_BH1780 This driver can also be built as a module. If so, the module will be called bh1780gli. +config SENSORS_BH1770 + tristate "BH1770GLC / SFH7770 combined ALS - Proximity sensor" + depends on I2C + ---help--- + Say Y here if you want to build a driver for BH1770GLC (ROHM) or + SFH7770 (Osram) combined ambient light and proximity sensor chip. + + To compile this driver as a module, choose M here: the + module will be called bh1770glc. If unsure, say N here. + +config SENSORS_APDS990X + tristate "APDS990X combined als and proximity sensors" + depends on I2C + default n + ---help--- + Say Y here if you want to build a driver for Avago APDS990x + combined ambient light and proximity sensor chip. + + To compile this driver as a module, choose M here: the + module will be called apds990x. If unsure, say N here. + config HMC6352 tristate "Honeywell HMC6352 compass" depends on I2C diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 9f2986b4da2..4be5c6fc5ef 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -16,6 +16,8 @@ obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o +obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o +obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o obj-$(CONFIG_SGI_IOC4) += ioc4.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o @@ -23,7 +25,9 @@ obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o obj-$(CONFIG_HP_ILO) += hpilo.o +obj-$(CONFIG_APDS9802ALS) += apds9802als.o obj-$(CONFIG_ISL29003) += isl29003.o +obj-$(CONFIG_ISL29020) += isl29020.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o obj-$(CONFIG_DS1682) += ds1682.o diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c index 374352af797..4ff73c21574 100644 --- a/drivers/misc/ad525x_dpot-i2c.c +++ b/drivers/misc/ad525x_dpot-i2c.c @@ -102,6 +102,8 @@ static const struct i2c_device_id ad_dpot_id[] = { {"ad5170", AD5170_ID}, {"ad5172", AD5172_ID}, {"ad5173", AD5173_ID}, + {"ad5272", AD5272_ID}, + {"ad5274", AD5274_ID}, {} }; MODULE_DEVICE_TABLE(i2c, ad_dpot_id); diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c index b8c6df9c843..7f9a55afe05 100644 --- a/drivers/misc/ad525x_dpot-spi.c +++ b/drivers/misc/ad525x_dpot-spi.c @@ -38,6 +38,8 @@ static const struct ad_dpot_id ad_dpot_spi_devlist[] = { {.name = "ad8402", .devid = AD8402_ID}, {.name = "ad8403", .devid = AD8403_ID}, {.name = "adn2850", .devid = ADN2850_ID}, + {.name = "ad5270", .devid = AD5270_ID}, + {.name = "ad5271", .devid = AD5271_ID}, {} }; @@ -53,13 +55,13 @@ static int write8(void *client, u8 val) static int write16(void *client, u8 reg, u8 val) { u8 data[2] = {reg, val}; - return spi_write(client, data, 1); + return spi_write(client, data, 2); } static int write24(void *client, u8 reg, u16 val) { u8 data[3] = {reg, val >> 8, val}; - return spi_write(client, data, 1); + return spi_write(client, data, 3); } static int read8(void *client) diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c index 5e6fa8449e8..7cb911028d0 100644 --- a/drivers/misc/ad525x_dpot.c +++ b/drivers/misc/ad525x_dpot.c @@ -29,9 +29,9 @@ * AD5262 2 256 20, 50, 200 * AD5263 4 256 20, 50, 200 * AD5290 1 256 10, 50, 100 - * AD5291 1 256 20 - * AD5292 1 1024 20 - * AD5293 1 1024 20 + * AD5291 1 256 20, 50, 100 (20-TP) + * AD5292 1 1024 20, 50, 100 (20-TP) + * AD5293 1 1024 20, 50, 100 * AD7376 1 128 10, 50, 100, 1M * AD8400 1 256 1, 10, 50, 100 * AD8402 2 256 1, 10, 50, 100 @@ -52,6 +52,10 @@ * AD5170 1 256 2.5, 10, 50, 100 (OTP) * AD5172 2 256 2.5, 10, 50, 100 (OTP) * AD5173 2 256 2.5, 10, 50, 100 (OTP) + * AD5270 1 1024 20, 50, 100 (50-TP) + * AD5271 1 256 20, 50, 100 (50-TP) + * AD5272 1 1024 20, 50, 100 (50-TP) + * AD5274 1 256 20, 50, 100 (50-TP) * * See Documentation/misc-devices/ad525x_dpot.txt for more info. * @@ -126,18 +130,38 @@ static inline int dpot_write_r8d16(struct dpot_data *dpot, u8 reg, u16 val) static s32 dpot_read_spi(struct dpot_data *dpot, u8 reg) { unsigned ctrl = 0; + int value; if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD))) { if (dpot->feat & F_RDACS_WONLY) return dpot->rdac_cache[reg & DPOT_RDAC_MASK]; - if (dpot->uid == DPOT_UID(AD5291_ID) || dpot->uid == DPOT_UID(AD5292_ID) || - dpot->uid == DPOT_UID(AD5293_ID)) - return dpot_read_r8d8(dpot, + dpot->uid == DPOT_UID(AD5293_ID)) { + + value = dpot_read_r8d8(dpot, DPOT_AD5291_READ_RDAC << 2); + if (dpot->uid == DPOT_UID(AD5291_ID)) + value = value >> 2; + + return value; + } else if (dpot->uid == DPOT_UID(AD5270_ID) || + dpot->uid == DPOT_UID(AD5271_ID)) { + + value = dpot_read_r8d8(dpot, + DPOT_AD5270_1_2_4_READ_RDAC << 2); + + if (value < 0) + return value; + + if (dpot->uid == DPOT_UID(AD5271_ID)) + value = value >> 2; + + return value; + } + ctrl = DPOT_SPI_READ_RDAC; } else if (reg & DPOT_ADDR_EEPROM) { ctrl = DPOT_SPI_READ_EEPROM; @@ -153,6 +177,7 @@ static s32 dpot_read_spi(struct dpot_data *dpot, u8 reg) static s32 dpot_read_i2c(struct dpot_data *dpot, u8 reg) { + int value; unsigned ctrl = 0; switch (dpot->uid) { case DPOT_UID(AD5246_ID): @@ -166,7 +191,7 @@ static s32 dpot_read_i2c(struct dpot_data *dpot, u8 reg) case DPOT_UID(AD5280_ID): case DPOT_UID(AD5282_ID): ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ? - 0 : DPOT_AD5291_RDAC_AB; + 0 : DPOT_AD5282_RDAC_AB; return dpot_read_r8d8(dpot, ctrl); case DPOT_UID(AD5170_ID): case DPOT_UID(AD5171_ID): @@ -175,8 +200,27 @@ static s32 dpot_read_i2c(struct dpot_data *dpot, u8 reg) case DPOT_UID(AD5172_ID): case DPOT_UID(AD5173_ID): ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ? - 0 : DPOT_AD5272_3_A0; + 0 : DPOT_AD5172_3_A0; return dpot_read_r8d8(dpot, ctrl); + case DPOT_UID(AD5272_ID): + case DPOT_UID(AD5274_ID): + dpot_write_r8d8(dpot, + (DPOT_AD5270_1_2_4_READ_RDAC << 2), 0); + + value = dpot_read_r8d16(dpot, + DPOT_AD5270_1_2_4_RDAC << 2); + + if (value < 0) + return value; + /* + * AD5272/AD5274 returns high byte first, however + * underling smbus expects low byte first. + */ + value = swab16(value); + + if (dpot->uid == DPOT_UID(AD5271_ID)) + value = value >> 2; + return value; default: if ((reg & DPOT_REG_TOL) || (dpot->max_pos > 256)) return dpot_read_r8d16(dpot, (reg & 0xF8) | @@ -198,7 +242,7 @@ static s32 dpot_write_spi(struct dpot_data *dpot, u8 reg, u16 value) { unsigned val = 0; - if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD))) { + if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD | DPOT_ADDR_OTP))) { if (dpot->feat & F_RDACS_WONLY) dpot->rdac_cache[reg & DPOT_RDAC_MASK] = value; @@ -219,11 +263,30 @@ static s32 dpot_write_spi(struct dpot_data *dpot, u8 reg, u16 value) } else { if (dpot->uid == DPOT_UID(AD5291_ID) || dpot->uid == DPOT_UID(AD5292_ID) || - dpot->uid == DPOT_UID(AD5293_ID)) + dpot->uid == DPOT_UID(AD5293_ID)) { + + dpot_write_r8d8(dpot, DPOT_AD5291_CTRLREG << 2, + DPOT_AD5291_UNLOCK_CMD); + + if (dpot->uid == DPOT_UID(AD5291_ID)) + value = value << 2; + return dpot_write_r8d8(dpot, (DPOT_AD5291_RDAC << 2) | (value >> 8), value & 0xFF); + } else if (dpot->uid == DPOT_UID(AD5270_ID) || + dpot->uid == DPOT_UID(AD5271_ID)) { + dpot_write_r8d8(dpot, + DPOT_AD5270_1_2_4_CTRLREG << 2, + DPOT_AD5270_1_2_4_UNLOCK_CMD); + + if (dpot->uid == DPOT_UID(AD5271_ID)) + value = value << 2; + return dpot_write_r8d8(dpot, + (DPOT_AD5270_1_2_4_RDAC << 2) | + (value >> 8), value & 0xFF); + } val = DPOT_SPI_RDAC | (reg & DPOT_RDAC_MASK); } } else if (reg & DPOT_ADDR_EEPROM) { @@ -243,6 +306,16 @@ static s32 dpot_write_spi(struct dpot_data *dpot, u8 reg, u16 value) val = DPOT_SPI_INC_ALL; break; } + } else if (reg & DPOT_ADDR_OTP) { + if (dpot->uid == DPOT_UID(AD5291_ID) || + dpot->uid == DPOT_UID(AD5292_ID)) { + return dpot_write_r8d8(dpot, + DPOT_AD5291_STORE_XTPM << 2, 0); + } else if (dpot->uid == DPOT_UID(AD5270_ID) || + dpot->uid == DPOT_UID(AD5271_ID)) { + return dpot_write_r8d8(dpot, + DPOT_AD5270_1_2_4_STORE_XTPM << 2, 0); + } } else BUG(); @@ -273,7 +346,7 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value) case DPOT_UID(AD5280_ID): case DPOT_UID(AD5282_ID): ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ? - 0 : DPOT_AD5291_RDAC_AB; + 0 : DPOT_AD5282_RDAC_AB; return dpot_write_r8d8(dpot, ctrl, value); break; case DPOT_UID(AD5171_ID): @@ -289,12 +362,12 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value) case DPOT_UID(AD5172_ID): case DPOT_UID(AD5173_ID): ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ? - 0 : DPOT_AD5272_3_A0; + 0 : DPOT_AD5172_3_A0; if (reg & DPOT_ADDR_OTP) { tmp = dpot_read_r8d16(dpot, ctrl); if (tmp >> 14) /* Ready to Program? */ return -EFAULT; - ctrl |= DPOT_AD5270_2_3_FUSE; + ctrl |= DPOT_AD5170_2_3_FUSE; } return dpot_write_r8d8(dpot, ctrl, value); break; @@ -303,10 +376,25 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value) tmp = dpot_read_r8d16(dpot, tmp); if (tmp >> 14) /* Ready to Program? */ return -EFAULT; - ctrl = DPOT_AD5270_2_3_FUSE; + ctrl = DPOT_AD5170_2_3_FUSE; } return dpot_write_r8d8(dpot, ctrl, value); break; + case DPOT_UID(AD5272_ID): + case DPOT_UID(AD5274_ID): + dpot_write_r8d8(dpot, DPOT_AD5270_1_2_4_CTRLREG << 2, + DPOT_AD5270_1_2_4_UNLOCK_CMD); + + if (reg & DPOT_ADDR_OTP) + return dpot_write_r8d8(dpot, + DPOT_AD5270_1_2_4_STORE_XTPM << 2, 0); + + if (dpot->uid == DPOT_UID(AD5274_ID)) + value = value << 2; + + return dpot_write_r8d8(dpot, (DPOT_AD5270_1_2_4_RDAC << 2) | + (value >> 8), value & 0xFF); + break; default: if (reg & DPOT_ADDR_CMD) return dpot_write_d8(dpot, reg); @@ -320,7 +408,6 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value) } } - static s32 dpot_write(struct dpot_data *dpot, u8 reg, u16 value) { if (dpot->feat & F_SPI) diff --git a/drivers/misc/ad525x_dpot.h b/drivers/misc/ad525x_dpot.h index 78b89fd2e2f..a662f5987b6 100644 --- a/drivers/misc/ad525x_dpot.h +++ b/drivers/misc/ad525x_dpot.h @@ -47,9 +47,9 @@ enum dpot_devid { AD5258_ID = DPOT_CONF(F_RDACS_RW_TOL, BRDAC0, 6, 0), /* I2C */ AD5259_ID = DPOT_CONF(F_RDACS_RW_TOL, BRDAC0, 8, 1), AD5251_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC, - BRDAC0 | BRDAC3, 6, 2), + BRDAC1 | BRDAC3, 6, 2), AD5252_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC, - BRDAC0 | BRDAC3, 8, 3), + BRDAC1 | BRDAC3, 8, 3), AD5253_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC, BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 6, 4), AD5254_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC, @@ -93,8 +93,10 @@ enum dpot_devid { BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 8, 23), AD5290_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT, BRDAC0, 8, 24), - AD5291_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 8, 25), - AD5292_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 10, 26), + AD5291_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT | F_CMD_OTP, + BRDAC0, 8, 25), + AD5292_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT | F_CMD_OTP, + BRDAC0, 10, 26), AD5293_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 10, 27), AD7376_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT, BRDAC0, 7, 28), @@ -122,6 +124,12 @@ enum dpot_devid { AD5170_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0, 8, 45), AD5172_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0 | BRDAC1, 8, 46), AD5173_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0 | BRDAC1, 8, 47), + AD5270_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP | F_SPI_16BIT, + BRDAC0, 10, 48), + AD5271_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP | F_SPI_16BIT, + BRDAC0, 8, 49), + AD5272_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0, 10, 50), + AD5274_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0, 8, 51), }; #define DPOT_RDAC0 0 @@ -165,15 +173,24 @@ enum dpot_devid { /* AD5291/2/3 use special commands */ #define DPOT_AD5291_RDAC 0x01 #define DPOT_AD5291_READ_RDAC 0x02 +#define DPOT_AD5291_STORE_XTPM 0x03 +#define DPOT_AD5291_CTRLREG 0x06 +#define DPOT_AD5291_UNLOCK_CMD 0x03 -/* AD524x use special commands */ -#define DPOT_AD5291_RDAC_AB 0x80 +/* AD5270/1/2/4 use special commands */ +#define DPOT_AD5270_1_2_4_RDAC 0x01 +#define DPOT_AD5270_1_2_4_READ_RDAC 0x02 +#define DPOT_AD5270_1_2_4_STORE_XTPM 0x03 +#define DPOT_AD5270_1_2_4_CTRLREG 0x07 +#define DPOT_AD5270_1_2_4_UNLOCK_CMD 0x03 + +#define DPOT_AD5282_RDAC_AB 0x80 #define DPOT_AD5273_FUSE 0x80 -#define DPOT_AD5270_2_3_FUSE 0x20 -#define DPOT_AD5270_2_3_OW 0x08 -#define DPOT_AD5272_3_A0 0x08 -#define DPOT_AD5270_2FUSE 0x80 +#define DPOT_AD5170_2_3_FUSE 0x20 +#define DPOT_AD5170_2_3_OW 0x08 +#define DPOT_AD5172_3_A0 0x08 +#define DPOT_AD5170_2FUSE 0x80 struct dpot_data; diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c new file mode 100644 index 00000000000..f9b91ba8900 --- /dev/null +++ b/drivers/misc/apds9802als.c @@ -0,0 +1,347 @@ +/* + * apds9802als.c - apds9802 ALS Driver + * + * Copyright (C) 2009 Intel Corp + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/sysfs.h> +#include <linux/pm_runtime.h> + +#define ALS_MIN_RANGE_VAL 1 +#define ALS_MAX_RANGE_VAL 2 +#define POWER_STA_ENABLE 1 +#define POWER_STA_DISABLE 0 + +#define DRIVER_NAME "apds9802als" + +struct als_data { + struct mutex mutex; +}; + +static ssize_t als_sensing_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int val; + + val = i2c_smbus_read_byte_data(client, 0x81); + if (val < 0) + return val; + if (val & 1) + return sprintf(buf, "4095\n"); + else + return sprintf(buf, "65535\n"); +} + +static int als_wait_for_data_ready(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + int retry = 10; + + do { + msleep(30); + ret = i2c_smbus_read_byte_data(client, 0x86); + } while (!(ret & 0x80) && retry--); + + if (!retry) { + dev_warn(dev, "timeout waiting for data ready\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static ssize_t als_lux0_input_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct als_data *data = i2c_get_clientdata(client); + int ret_val; + int temp; + + /* Protect against parallel reads */ + pm_runtime_get_sync(dev); + mutex_lock(&data->mutex); + + /* clear EOC interrupt status */ + i2c_smbus_write_byte(client, 0x40); + /* start measurement */ + temp = i2c_smbus_read_byte_data(client, 0x81); + i2c_smbus_write_byte_data(client, 0x81, temp | 0x08); + + ret_val = als_wait_for_data_ready(dev); + if (ret_val < 0) + goto failed; + + temp = i2c_smbus_read_byte_data(client, 0x8C); /* LSB data */ + if (temp < 0) { + ret_val = temp; + goto failed; + } + ret_val = i2c_smbus_read_byte_data(client, 0x8D); /* MSB data */ + if (ret_val < 0) + goto failed; + + mutex_unlock(&data->mutex); + pm_runtime_put_sync(dev); + + temp = (ret_val << 8) | temp; + return sprintf(buf, "%d\n", temp); +failed: + mutex_unlock(&data->mutex); + pm_runtime_put_sync(dev); + return ret_val; +} + +static ssize_t als_sensing_range_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct als_data *data = i2c_get_clientdata(client); + unsigned int ret_val; + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + if (val < 4096) + val = 1; + else if (val < 65536) + val = 2; + else + return -ERANGE; + + pm_runtime_get_sync(dev); + + /* Make sure nobody else reads/modifies/writes 0x81 while we + are active */ + mutex_lock(&data->mutex); + + ret_val = i2c_smbus_read_byte_data(client, 0x81); + if (ret_val < 0) + goto fail; + + /* Reset the bits before setting them */ + ret_val = ret_val & 0xFA; + + if (val == 1) /* Setting detection range up to 4k LUX */ + ret_val = (ret_val | 0x01); + else /* Setting detection range up to 64k LUX*/ + ret_val = (ret_val | 0x00); + + ret_val = i2c_smbus_write_byte_data(client, 0x81, ret_val); + + if (ret_val >= 0) { + /* All OK */ + mutex_unlock(&data->mutex); + pm_runtime_put_sync(dev); + return count; + } +fail: + mutex_unlock(&data->mutex); + pm_runtime_put_sync(dev); + return ret_val; +} + +static int als_set_power_state(struct i2c_client *client, bool on_off) +{ + int ret_val; + struct als_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->mutex); + ret_val = i2c_smbus_read_byte_data(client, 0x80); + if (ret_val < 0) + goto fail; + if (on_off) + ret_val = ret_val | 0x01; + else + ret_val = ret_val & 0xFE; + ret_val = i2c_smbus_write_byte_data(client, 0x80, ret_val); +fail: + mutex_unlock(&data->mutex); + return ret_val; +} + +static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR, + als_sensing_range_show, als_sensing_range_store); +static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux0_input_data_show, NULL); + +static struct attribute *mid_att_als[] = { + &dev_attr_lux0_sensor_range.attr, + &dev_attr_lux0_input.attr, + NULL +}; + +static struct attribute_group m_als_gr = { + .name = "apds9802als", + .attrs = mid_att_als +}; + +static int als_set_default_config(struct i2c_client *client) +{ + int ret_val; + /* Write the command and then switch on */ + ret_val = i2c_smbus_write_byte_data(client, 0x80, 0x01); + if (ret_val < 0) { + dev_err(&client->dev, "failed default switch on write\n"); + return ret_val; + } + /* detection range: 1~64K Lux, maunal measurement */ + ret_val = i2c_smbus_write_byte_data(client, 0x81, 0x08); + if (ret_val < 0) + dev_err(&client->dev, "failed default LUX on write\n"); + + /* We always get 0 for the 1st measurement after system power on, + * so make sure it is finished before user asks for data. + */ + als_wait_for_data_ready(&client->dev); + + return ret_val; +} + +static int apds9802als_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int res; + struct als_data *data; + + data = kzalloc(sizeof(struct als_data), GFP_KERNEL); + if (data == NULL) { + dev_err(&client->dev, "Memory allocation failed\n"); + return -ENOMEM; + } + i2c_set_clientdata(client, data); + res = sysfs_create_group(&client->dev.kobj, &m_als_gr); + if (res) { + dev_err(&client->dev, "device create file failed\n"); + goto als_error1; + } + dev_info(&client->dev, "ALS chip found\n"); + als_set_default_config(client); + mutex_init(&data->mutex); + + pm_runtime_enable(&client->dev); + pm_runtime_get(&client->dev); + pm_runtime_put(&client->dev); + + return res; +als_error1: + i2c_set_clientdata(client, NULL); + kfree(data); + return res; +} + +static int apds9802als_remove(struct i2c_client *client) +{ + struct als_data *data = i2c_get_clientdata(client); + + als_set_power_state(client, false); + sysfs_remove_group(&client->dev.kobj, &m_als_gr); + kfree(data); + return 0; +} + +#ifdef CONFIG_PM +static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg) +{ + als_set_power_state(client, false); + return 0; +} + +static int apds9802als_resume(struct i2c_client *client) +{ + als_set_default_config(client); + + pm_runtime_get(&client->dev); + pm_runtime_put(&client->dev); + return 0; +} + +static int apds9802als_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + als_set_power_state(client, false); + return 0; +} + +static int apds9802als_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + als_set_power_state(client, true); + return 0; +} + +static const struct dev_pm_ops apds9802als_pm_ops = { + .runtime_suspend = apds9802als_runtime_suspend, + .runtime_resume = apds9802als_runtime_resume, +}; + +#define APDS9802ALS_PM_OPS (&apds9802als_pm_ops) + +#else /* CONFIG_PM */ +#define apds9802als_suspend NULL +#define apds9802als_resume NULL +#define APDS9802ALS_PM_OPS NULL +#endif /* CONFIG_PM */ + +static struct i2c_device_id apds9802als_id[] = { + { DRIVER_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, apds9802als_id); + +static struct i2c_driver apds9802als_driver = { + .driver = { + .name = DRIVER_NAME, + .pm = APDS9802ALS_PM_OPS, + }, + .probe = apds9802als_probe, + .remove = apds9802als_remove, + .suspend = apds9802als_suspend, + .resume = apds9802als_resume, + .id_table = apds9802als_id, +}; + +static int __init sensor_apds9802als_init(void) +{ + return i2c_add_driver(&apds9802als_driver); +} + +static void __exit sensor_apds9802als_exit(void) +{ + i2c_del_driver(&apds9802als_driver); +} +module_init(sensor_apds9802als_init); +module_exit(sensor_apds9802als_exit); + +MODULE_AUTHOR("Anantha Narayanan <Anantha.Narayanan@intel.com"); +MODULE_DESCRIPTION("Avago apds9802als ALS Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c new file mode 100644 index 00000000000..200311fea36 --- /dev/null +++ b/drivers/misc/apds990x.c @@ -0,0 +1,1295 @@ +/* + * This file is part of the APDS990x sensor driver. + * Chip is combined proximity and ambient light sensor. + * + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/slab.h> +#include <linux/i2c/apds990x.h> + +/* Register map */ +#define APDS990X_ENABLE 0x00 /* Enable of states and interrupts */ +#define APDS990X_ATIME 0x01 /* ALS ADC time */ +#define APDS990X_PTIME 0x02 /* Proximity ADC time */ +#define APDS990X_WTIME 0x03 /* Wait time */ +#define APDS990X_AILTL 0x04 /* ALS interrupt low threshold low byte */ +#define APDS990X_AILTH 0x05 /* ALS interrupt low threshold hi byte */ +#define APDS990X_AIHTL 0x06 /* ALS interrupt hi threshold low byte */ +#define APDS990X_AIHTH 0x07 /* ALS interrupt hi threshold hi byte */ +#define APDS990X_PILTL 0x08 /* Proximity interrupt low threshold low byte */ +#define APDS990X_PILTH 0x09 /* Proximity interrupt low threshold hi byte */ +#define APDS990X_PIHTL 0x0a /* Proximity interrupt hi threshold low byte */ +#define APDS990X_PIHTH 0x0b /* Proximity interrupt hi threshold hi byte */ +#define APDS990X_PERS 0x0c /* Interrupt persistence filters */ +#define APDS990X_CONFIG 0x0d /* Configuration */ +#define APDS990X_PPCOUNT 0x0e /* Proximity pulse count */ +#define APDS990X_CONTROL 0x0f /* Gain control register */ +#define APDS990X_REV 0x11 /* Revision Number */ +#define APDS990X_ID 0x12 /* Device ID */ +#define APDS990X_STATUS 0x13 /* Device status */ +#define APDS990X_CDATAL 0x14 /* Clear ADC low data register */ +#define APDS990X_CDATAH 0x15 /* Clear ADC high data register */ +#define APDS990X_IRDATAL 0x16 /* IR ADC low data register */ +#define APDS990X_IRDATAH 0x17 /* IR ADC high data register */ +#define APDS990X_PDATAL 0x18 /* Proximity ADC low data register */ +#define APDS990X_PDATAH 0x19 /* Proximity ADC high data register */ + +/* Control */ +#define APDS990X_MAX_AGAIN 3 + +/* Enable register */ +#define APDS990X_EN_PIEN (0x1 << 5) +#define APDS990X_EN_AIEN (0x1 << 4) +#define APDS990X_EN_WEN (0x1 << 3) +#define APDS990X_EN_PEN (0x1 << 2) +#define APDS990X_EN_AEN (0x1 << 1) +#define APDS990X_EN_PON (0x1 << 0) +#define APDS990X_EN_DISABLE_ALL 0 + +/* Status register */ +#define APDS990X_ST_PINT (0x1 << 5) +#define APDS990X_ST_AINT (0x1 << 4) + +/* I2C access types */ +#define APDS990x_CMD_TYPE_MASK (0x03 << 5) +#define APDS990x_CMD_TYPE_RB (0x00 << 5) /* Repeated byte */ +#define APDS990x_CMD_TYPE_INC (0x01 << 5) /* Auto increment */ +#define APDS990x_CMD_TYPE_SPE (0x03 << 5) /* Special function */ + +#define APDS990x_ADDR_SHIFT 0 +#define APDS990x_CMD 0x80 + +/* Interrupt ack commands */ +#define APDS990X_INT_ACK_ALS 0x6 +#define APDS990X_INT_ACK_PS 0x5 +#define APDS990X_INT_ACK_BOTH 0x7 + +/* ptime */ +#define APDS990X_PTIME_DEFAULT 0xff /* Recommended conversion time 2.7ms*/ + +/* wtime */ +#define APDS990X_WTIME_DEFAULT 0xee /* ~50ms wait time */ + +#define APDS990X_TIME_TO_ADC 1024 /* One timetick as ADC count value */ + +/* Persistence */ +#define APDS990X_APERS_SHIFT 0 +#define APDS990X_PPERS_SHIFT 4 + +/* Supported ID:s */ +#define APDS990X_ID_0 0x0 +#define APDS990X_ID_4 0x4 +#define APDS990X_ID_29 0x29 + +/* pgain and pdiode settings */ +#define APDS_PGAIN_1X 0x0 +#define APDS_PDIODE_IR 0x2 + +#define APDS990X_LUX_OUTPUT_SCALE 10 + +/* Reverse chip factors for threshold calculation */ +struct reverse_factors { + u32 afactor; + int cf1; + int irf1; + int cf2; + int irf2; +}; + +struct apds990x_chip { + struct apds990x_platform_data *pdata; + struct i2c_client *client; + struct mutex mutex; /* avoid parallel access */ + struct regulator_bulk_data regs[2]; + wait_queue_head_t wait; + + int prox_en; + bool prox_continuous_mode; + bool lux_wait_fresh_res; + + /* Chip parameters */ + struct apds990x_chip_factors cf; + struct reverse_factors rcf; + u16 atime; /* als integration time */ + u16 arate; /* als reporting rate */ + u16 a_max_result; /* Max possible ADC value with current atime */ + u8 again_meas; /* Gain used in last measurement */ + u8 again_next; /* Next calculated gain */ + u8 pgain; + u8 pdiode; + u8 pdrive; + u8 lux_persistence; + u8 prox_persistence; + + u32 lux_raw; + u32 lux; + u16 lux_clear; + u16 lux_ir; + u16 lux_calib; + u32 lux_thres_hi; + u32 lux_thres_lo; + + u32 prox_thres; + u16 prox_data; + u16 prox_calib; + + char chipname[10]; + u8 revision; +}; + +#define APDS_CALIB_SCALER 8192 +#define APDS_LUX_NEUTRAL_CALIB_VALUE (1 * APDS_CALIB_SCALER) +#define APDS_PROX_NEUTRAL_CALIB_VALUE (1 * APDS_CALIB_SCALER) + +#define APDS_PROX_DEF_THRES 600 +#define APDS_PROX_HYSTERESIS 50 +#define APDS_LUX_DEF_THRES_HI 101 +#define APDS_LUX_DEF_THRES_LO 100 +#define APDS_DEFAULT_PROX_PERS 1 + +#define APDS_TIMEOUT 2000 +#define APDS_STARTUP_DELAY 25000 /* us */ +#define APDS_RANGE 65535 +#define APDS_PROX_RANGE 1023 +#define APDS_LUX_GAIN_LO_LIMIT 100 +#define APDS_LUX_GAIN_LO_LIMIT_STRICT 25 + +#define TIMESTEP 87 /* 2.7ms is about 87 / 32 */ +#define TIME_STEP_SCALER 32 + +#define APDS_LUX_AVERAGING_TIME 50 /* tolerates 50/60Hz ripple */ +#define APDS_LUX_DEFAULT_RATE 200 + +static const u8 again[] = {1, 8, 16, 120}; /* ALS gain steps */ +static const u8 ir_currents[] = {100, 50, 25, 12}; /* IRled currents in mA */ + +/* Following two tables must match i.e 10Hz rate means 1 as persistence value */ +static const u16 arates_hz[] = {10, 5, 2, 1}; +static const u8 apersis[] = {1, 2, 4, 5}; + +/* Regulators */ +static const char reg_vcc[] = "Vdd"; +static const char reg_vled[] = "Vled"; + +static int apds990x_read_byte(struct apds990x_chip *chip, u8 reg, u8 *data) +{ + struct i2c_client *client = chip->client; + s32 ret; + + reg &= ~APDS990x_CMD_TYPE_MASK; + reg |= APDS990x_CMD | APDS990x_CMD_TYPE_RB; + + ret = i2c_smbus_read_byte_data(client, reg); + *data = ret; + return (int)ret; +} + +static int apds990x_read_word(struct apds990x_chip *chip, u8 reg, u16 *data) +{ + struct i2c_client *client = chip->client; + s32 ret; + + reg &= ~APDS990x_CMD_TYPE_MASK; + reg |= APDS990x_CMD | APDS990x_CMD_TYPE_INC; + + ret = i2c_smbus_read_word_data(client, reg); + *data = ret; + return (int)ret; +} + +static int apds990x_write_byte(struct apds990x_chip *chip, u8 reg, u8 data) +{ + struct i2c_client *client = chip->client; + s32 ret; + + reg &= ~APDS990x_CMD_TYPE_MASK; + reg |= APDS990x_CMD | APDS990x_CMD_TYPE_RB; + + ret = i2c_smbus_write_byte_data(client, reg, data); + return (int)ret; +} + +static int apds990x_write_word(struct apds990x_chip *chip, u8 reg, u16 data) +{ + struct i2c_client *client = chip->client; + s32 ret; + + reg &= ~APDS990x_CMD_TYPE_MASK; + reg |= APDS990x_CMD | APDS990x_CMD_TYPE_INC; + + ret = i2c_smbus_write_word_data(client, reg, data); + return (int)ret; +} + +static int apds990x_mode_on(struct apds990x_chip *chip) +{ + /* ALS is mandatory, proximity optional */ + u8 reg = APDS990X_EN_AIEN | APDS990X_EN_PON | APDS990X_EN_AEN | + APDS990X_EN_WEN; + + if (chip->prox_en) + reg |= APDS990X_EN_PIEN | APDS990X_EN_PEN; + + return apds990x_write_byte(chip, APDS990X_ENABLE, reg); +} + +static u16 apds990x_lux_to_threshold(struct apds990x_chip *chip, u32 lux) +{ + u32 thres; + u32 cpl; + u32 ir; + + if (lux == 0) + return 0; + else if (lux == APDS_RANGE) + return APDS_RANGE; + + /* + * Reported LUX value is a combination of the IR and CLEAR channel + * values. However, interrupt threshold is only for clear channel. + * This function approximates needed HW threshold value for a given + * LUX value in the current lightning type. + * IR level compared to visible light varies heavily depending on the + * source of the light + * + * Calculate threshold value for the next measurement period. + * Math: threshold = lux * cpl where + * cpl = atime * again / (glass_attenuation * device_factor) + * (count-per-lux) + * + * First remove calibration. Division by four is to avoid overflow + */ + lux = lux * (APDS_CALIB_SCALER / 4) / (chip->lux_calib / 4); + + /* Multiplication by 64 is to increase accuracy */ + cpl = ((u32)chip->atime * (u32)again[chip->again_next] * + APDS_PARAM_SCALE * 64) / (chip->cf.ga * chip->cf.df); + + thres = lux * cpl / 64; + /* + * Convert IR light from the latest result to match with + * new gain step. This helps to adapt with the current + * source of light. + */ + ir = (u32)chip->lux_ir * (u32)again[chip->again_next] / + (u32)again[chip->again_meas]; + + /* + * Compensate count with IR light impact + * IAC1 > IAC2 (see apds990x_get_lux for formulas) + */ + if (chip->lux_clear * APDS_PARAM_SCALE >= + chip->rcf.afactor * chip->lux_ir) + thres = (chip->rcf.cf1 * thres + chip->rcf.irf1 * ir) / + APDS_PARAM_SCALE; + else + thres = (chip->rcf.cf2 * thres + chip->rcf.irf2 * ir) / + APDS_PARAM_SCALE; + + if (thres >= chip->a_max_result) + thres = chip->a_max_result - 1; + return thres; +} + +static inline int apds990x_set_atime(struct apds990x_chip *chip, u32 time_ms) +{ + u8 reg_value; + + chip->atime = time_ms; + /* Formula is specified in the data sheet */ + reg_value = 256 - ((time_ms * TIME_STEP_SCALER) / TIMESTEP); + /* Calculate max ADC value for given integration time */ + chip->a_max_result = (u16)(256 - reg_value) * APDS990X_TIME_TO_ADC; + return apds990x_write_byte(chip, APDS990X_ATIME, reg_value); +} + +/* Called always with mutex locked */ +static int apds990x_refresh_pthres(struct apds990x_chip *chip, int data) +{ + int ret, lo, hi; + + /* If the chip is not in use, don't try to access it */ + if (pm_runtime_suspended(&chip->client->dev)) + return 0; + + if (data < chip->prox_thres) { + lo = 0; + hi = chip->prox_thres; + } else { + lo = chip->prox_thres - APDS_PROX_HYSTERESIS; + if (chip->prox_continuous_mode) + hi = chip->prox_thres; + else + hi = APDS_RANGE; + } + + ret = apds990x_write_word(chip, APDS990X_PILTL, lo); + ret |= apds990x_write_word(chip, APDS990X_PIHTL, hi); + return ret; +} + +/* Called always with mutex locked */ +static int apds990x_refresh_athres(struct apds990x_chip *chip) +{ + int ret; + /* If the chip is not in use, don't try to access it */ + if (pm_runtime_suspended(&chip->client->dev)) + return 0; + + ret = apds990x_write_word(chip, APDS990X_AILTL, + apds990x_lux_to_threshold(chip, chip->lux_thres_lo)); + ret |= apds990x_write_word(chip, APDS990X_AIHTL, + apds990x_lux_to_threshold(chip, chip->lux_thres_hi)); + + return ret; +} + +/* Called always with mutex locked */ +static void apds990x_force_a_refresh(struct apds990x_chip *chip) +{ + /* This will force ALS interrupt after the next measurement. */ + apds990x_write_word(chip, APDS990X_AILTL, APDS_LUX_DEF_THRES_LO); + apds990x_write_word(chip, APDS990X_AIHTL, APDS_LUX_DEF_THRES_HI); +} + +/* Called always with mutex locked */ +static void apds990x_force_p_refresh(struct apds990x_chip *chip) +{ + /* This will force proximity interrupt after the next measurement. */ + apds990x_write_word(chip, APDS990X_PILTL, APDS_PROX_DEF_THRES - 1); + apds990x_write_word(chip, APDS990X_PIHTL, APDS_PROX_DEF_THRES); +} + +/* Called always with mutex locked */ +static int apds990x_calc_again(struct apds990x_chip *chip) +{ + int curr_again = chip->again_meas; + int next_again = chip->again_meas; + int ret = 0; + + /* Calculate suitable als gain */ + if (chip->lux_clear == chip->a_max_result) + next_again -= 2; /* ALS saturated. Decrease gain by 2 steps */ + else if (chip->lux_clear > chip->a_max_result / 2) + next_again--; + else if (chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT_STRICT) + next_again += 2; /* Too dark. Increase gain by 2 steps */ + else if (chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT) + next_again++; + + /* Limit gain to available range */ + if (next_again < 0) + next_again = 0; + else if (next_again > APDS990X_MAX_AGAIN) + next_again = APDS990X_MAX_AGAIN; + + /* Let's check can we trust the measured result */ + if (chip->lux_clear == chip->a_max_result) + /* Result can be totally garbage due to saturation */ + ret = -ERANGE; + else if (next_again != curr_again && + chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT_STRICT) + /* + * Gain is changed and measurement result is very small. + * Result can be totally garbage due to underflow + */ + ret = -ERANGE; + + chip->again_next = next_again; + apds990x_write_byte(chip, APDS990X_CONTROL, + (chip->pdrive << 6) | + (chip->pdiode << 4) | + (chip->pgain << 2) | + (chip->again_next << 0)); + + /* + * Error means bad result -> re-measurement is needed. The forced + * refresh uses fastest possible persistence setting to get result + * as soon as possible. + */ + if (ret < 0) + apds990x_force_a_refresh(chip); + else + apds990x_refresh_athres(chip); + + return ret; +} + +/* Called always with mutex locked */ +static int apds990x_get_lux(struct apds990x_chip *chip, int clear, int ir) +{ + int iac, iac1, iac2; /* IR adjusted counts */ + u32 lpc; /* Lux per count */ + + /* Formulas: + * iac1 = CF1 * CLEAR_CH - IRF1 * IR_CH + * iac2 = CF2 * CLEAR_CH - IRF2 * IR_CH + */ + iac1 = (chip->cf.cf1 * clear - chip->cf.irf1 * ir) / APDS_PARAM_SCALE; + iac2 = (chip->cf.cf2 * clear - chip->cf.irf2 * ir) / APDS_PARAM_SCALE; + + iac = max(iac1, iac2); + iac = max(iac, 0); + + lpc = APDS990X_LUX_OUTPUT_SCALE * (chip->cf.df * chip->cf.ga) / + (u32)(again[chip->again_meas] * (u32)chip->atime); + + return (iac * lpc) / APDS_PARAM_SCALE; +} + +static int apds990x_ack_int(struct apds990x_chip *chip, u8 mode) +{ + struct i2c_client *client = chip->client; + s32 ret; + u8 reg = APDS990x_CMD | APDS990x_CMD_TYPE_SPE; + + switch (mode & (APDS990X_ST_AINT | APDS990X_ST_PINT)) { + case APDS990X_ST_AINT: + reg |= APDS990X_INT_ACK_ALS; + break; + case APDS990X_ST_PINT: + reg |= APDS990X_INT_ACK_PS; + break; + default: + reg |= APDS990X_INT_ACK_BOTH; + break; + } + + ret = i2c_smbus_read_byte_data(client, reg); + return (int)ret; +} + +static irqreturn_t apds990x_irq(int irq, void *data) +{ + struct apds990x_chip *chip = data; + u8 status; + + apds990x_read_byte(chip, APDS990X_STATUS, &status); + apds990x_ack_int(chip, status); + + mutex_lock(&chip->mutex); + if (!pm_runtime_suspended(&chip->client->dev)) { + if (status & APDS990X_ST_AINT) { + apds990x_read_word(chip, APDS990X_CDATAL, + &chip->lux_clear); + apds990x_read_word(chip, APDS990X_IRDATAL, + &chip->lux_ir); + /* Store used gain for calculations */ + chip->again_meas = chip->again_next; + + chip->lux_raw = apds990x_get_lux(chip, + chip->lux_clear, + chip->lux_ir); + + if (apds990x_calc_again(chip) == 0) { + /* Result is valid */ + chip->lux = chip->lux_raw; + chip->lux_wait_fresh_res = false; + wake_up(&chip->wait); + sysfs_notify(&chip->client->dev.kobj, + NULL, "lux0_input"); + } + } + + if ((status & APDS990X_ST_PINT) && chip->prox_en) { + u16 clr_ch; + + apds990x_read_word(chip, APDS990X_CDATAL, &clr_ch); + /* + * If ALS channel is saturated at min gain, + * proximity gives false posivite values. + * Just ignore them. + */ + if (chip->again_meas == 0 && + clr_ch == chip->a_max_result) + chip->prox_data = 0; + else + apds990x_read_word(chip, + APDS990X_PDATAL, + &chip->prox_data); + + apds990x_refresh_pthres(chip, chip->prox_data); + if (chip->prox_data < chip->prox_thres) + chip->prox_data = 0; + else if (!chip->prox_continuous_mode) + chip->prox_data = APDS_PROX_RANGE; + sysfs_notify(&chip->client->dev.kobj, + NULL, "prox0_raw"); + } + } + mutex_unlock(&chip->mutex); + return IRQ_HANDLED; +} + +static int apds990x_configure(struct apds990x_chip *chip) +{ + /* It is recommended to use disabled mode during these operations */ + apds990x_write_byte(chip, APDS990X_ENABLE, APDS990X_EN_DISABLE_ALL); + + /* conversion and wait times for different state machince states */ + apds990x_write_byte(chip, APDS990X_PTIME, APDS990X_PTIME_DEFAULT); + apds990x_write_byte(chip, APDS990X_WTIME, APDS990X_WTIME_DEFAULT); + apds990x_set_atime(chip, APDS_LUX_AVERAGING_TIME); + + apds990x_write_byte(chip, APDS990X_CONFIG, 0); + + /* Persistence levels */ + apds990x_write_byte(chip, APDS990X_PERS, + (chip->lux_persistence << APDS990X_APERS_SHIFT) | + (chip->prox_persistence << APDS990X_PPERS_SHIFT)); + + apds990x_write_byte(chip, APDS990X_PPCOUNT, chip->pdata->ppcount); + + /* Start with relatively small gain */ + chip->again_meas = 1; + chip->again_next = 1; + apds990x_write_byte(chip, APDS990X_CONTROL, + (chip->pdrive << 6) | + (chip->pdiode << 4) | + (chip->pgain << 2) | + (chip->again_next << 0)); + return 0; +} + +static int apds990x_detect(struct apds990x_chip *chip) +{ + struct i2c_client *client = chip->client; + int ret; + u8 id; + + ret = apds990x_read_byte(chip, APDS990X_ID, &id); + if (ret < 0) { + dev_err(&client->dev, "ID read failed\n"); + return ret; + } + + ret = apds990x_read_byte(chip, APDS990X_REV, &chip->revision); + if (ret < 0) { + dev_err(&client->dev, "REV read failed\n"); + return ret; + } + + switch (id) { + case APDS990X_ID_0: + case APDS990X_ID_4: + case APDS990X_ID_29: + snprintf(chip->chipname, sizeof(chip->chipname), "APDS-990x"); + break; + default: + ret = -ENODEV; + break; + } + return ret; +} + +static int apds990x_chip_on(struct apds990x_chip *chip) +{ + int err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), + chip->regs); + if (err < 0) + return err; + + usleep_range(APDS_STARTUP_DELAY, 2 * APDS_STARTUP_DELAY); + + /* Refresh all configs in case of regulators were off */ + chip->prox_data = 0; + apds990x_configure(chip); + apds990x_mode_on(chip); + return 0; +} + +static int apds990x_chip_off(struct apds990x_chip *chip) +{ + apds990x_write_byte(chip, APDS990X_ENABLE, APDS990X_EN_DISABLE_ALL); + regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); + return 0; +} + +static ssize_t apds990x_lux_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + ssize_t ret; + u32 result; + long timeout; + + if (pm_runtime_suspended(dev)) + return -EIO; + + timeout = wait_event_interruptible_timeout(chip->wait, + !chip->lux_wait_fresh_res, + msecs_to_jiffies(APDS_TIMEOUT)); + if (!timeout) + return -EIO; + + mutex_lock(&chip->mutex); + result = (chip->lux * chip->lux_calib) / APDS_CALIB_SCALER; + if (result > (APDS_RANGE * APDS990X_LUX_OUTPUT_SCALE)) + result = APDS_RANGE * APDS990X_LUX_OUTPUT_SCALE; + + ret = sprintf(buf, "%d.%d\n", + result / APDS990X_LUX_OUTPUT_SCALE, + result % APDS990X_LUX_OUTPUT_SCALE); + mutex_unlock(&chip->mutex); + return ret; +} + +static DEVICE_ATTR(lux0_input, S_IRUGO, apds990x_lux_show, NULL); + +static ssize_t apds990x_lux_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", APDS_RANGE); +} + +static DEVICE_ATTR(lux0_sensor_range, S_IRUGO, apds990x_lux_range_show, NULL); + +static ssize_t apds990x_lux_calib_format_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", APDS_CALIB_SCALER); +} + +static DEVICE_ATTR(lux0_calibscale_default, S_IRUGO, + apds990x_lux_calib_format_show, NULL); + +static ssize_t apds990x_lux_calib_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", chip->lux_calib); +} + +static ssize_t apds990x_lux_calib_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + unsigned long value; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + + if (chip->lux_calib > APDS_RANGE) + return -EINVAL; + + chip->lux_calib = value; + + return len; +} + +static DEVICE_ATTR(lux0_calibscale, S_IRUGO | S_IWUSR, apds990x_lux_calib_show, + apds990x_lux_calib_store); + +static ssize_t apds990x_rate_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + int pos = 0; + for (i = 0; i < ARRAY_SIZE(arates_hz); i++) + pos += sprintf(buf + pos, "%d ", arates_hz[i]); + sprintf(buf + pos - 1, "\n"); + return pos; +} + +static ssize_t apds990x_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", chip->arate); +} + +static int apds990x_set_arate(struct apds990x_chip *chip, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(arates_hz); i++) + if (rate >= arates_hz[i]) + break; + + if (i == ARRAY_SIZE(arates_hz)) + return -EINVAL; + + /* Pick up corresponding persistence value */ + chip->lux_persistence = apersis[i]; + chip->arate = arates_hz[i]; + + /* If the chip is not in use, don't try to access it */ + if (pm_runtime_suspended(&chip->client->dev)) + return 0; + + /* Persistence levels */ + return apds990x_write_byte(chip, APDS990X_PERS, + (chip->lux_persistence << APDS990X_APERS_SHIFT) | + (chip->prox_persistence << APDS990X_PPERS_SHIFT)); +} + +static ssize_t apds990x_rate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + unsigned long value; + int ret; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + + mutex_lock(&chip->mutex); + ret = apds990x_set_arate(chip, value); + mutex_unlock(&chip->mutex); + + if (ret < 0) + return ret; + return len; +} + +static DEVICE_ATTR(lux0_rate_avail, S_IRUGO, apds990x_rate_avail, NULL); + +static DEVICE_ATTR(lux0_rate, S_IRUGO | S_IWUSR, apds990x_rate_show, + apds990x_rate_store); + +static ssize_t apds990x_prox_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct apds990x_chip *chip = dev_get_drvdata(dev); + if (pm_runtime_suspended(dev) || !chip->prox_en) + return -EIO; + + mutex_lock(&chip->mutex); + ret = sprintf(buf, "%d\n", chip->prox_data); + mutex_unlock(&chip->mutex); + return ret; +} + +static DEVICE_ATTR(prox0_raw, S_IRUGO, apds990x_prox_show, NULL); + +static ssize_t apds990x_prox_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", APDS_PROX_RANGE); +} + +static DEVICE_ATTR(prox0_sensor_range, S_IRUGO, apds990x_prox_range_show, NULL); + +static ssize_t apds990x_prox_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", chip->prox_en); +} + +static ssize_t apds990x_prox_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + unsigned long value; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + + mutex_lock(&chip->mutex); + + if (!chip->prox_en) + chip->prox_data = 0; + + if (value) + chip->prox_en++; + else if (chip->prox_en > 0) + chip->prox_en--; + + if (!pm_runtime_suspended(dev)) + apds990x_mode_on(chip); + mutex_unlock(&chip->mutex); + return len; +} + +static DEVICE_ATTR(prox0_raw_en, S_IRUGO | S_IWUSR, apds990x_prox_enable_show, + apds990x_prox_enable_store); + +static const char reporting_modes[][9] = {"trigger", "periodic"}; + +static ssize_t apds990x_prox_reporting_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", + reporting_modes[!!chip->prox_continuous_mode]); +} + +static ssize_t apds990x_prox_reporting_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + + if (sysfs_streq(buf, reporting_modes[0])) + chip->prox_continuous_mode = 0; + else if (sysfs_streq(buf, reporting_modes[1])) + chip->prox_continuous_mode = 1; + else + return -EINVAL; + return len; +} + +static DEVICE_ATTR(prox0_reporting_mode, S_IRUGO | S_IWUSR, + apds990x_prox_reporting_mode_show, + apds990x_prox_reporting_mode_store); + +static ssize_t apds990x_prox_reporting_avail_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s %s\n", reporting_modes[0], reporting_modes[1]); +} + +static DEVICE_ATTR(prox0_reporting_mode_avail, S_IRUGO | S_IWUSR, + apds990x_prox_reporting_avail_show, NULL); + + +static ssize_t apds990x_lux_thresh_above_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", chip->lux_thres_hi); +} + +static ssize_t apds990x_lux_thresh_below_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", chip->lux_thres_lo); +} + +static ssize_t apds990x_set_lux_thresh(struct apds990x_chip *chip, u32 *target, + const char *buf) +{ + int ret = 0; + unsigned long thresh; + + if (strict_strtoul(buf, 0, &thresh)) + return -EINVAL; + + if (thresh > APDS_RANGE) + return -EINVAL; + + mutex_lock(&chip->mutex); + *target = thresh; + /* + * Don't update values in HW if we are still waiting for + * first interrupt to come after device handle open call. + */ + if (!chip->lux_wait_fresh_res) + apds990x_refresh_athres(chip); + mutex_unlock(&chip->mutex); + return ret; + +} + +static ssize_t apds990x_lux_thresh_above_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + int ret = apds990x_set_lux_thresh(chip, &chip->lux_thres_hi, buf); + if (ret < 0) + return ret; + return len; +} + +static ssize_t apds990x_lux_thresh_below_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + int ret = apds990x_set_lux_thresh(chip, &chip->lux_thres_lo, buf); + if (ret < 0) + return ret; + return len; +} + +static DEVICE_ATTR(lux0_thresh_above_value, S_IRUGO | S_IWUSR, + apds990x_lux_thresh_above_show, + apds990x_lux_thresh_above_store); + +static DEVICE_ATTR(lux0_thresh_below_value, S_IRUGO | S_IWUSR, + apds990x_lux_thresh_below_show, + apds990x_lux_thresh_below_store); + +static ssize_t apds990x_prox_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", chip->prox_thres); +} + +static ssize_t apds990x_prox_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + unsigned long value; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + + if ((value > APDS_RANGE) || (value == 0) || + (value < APDS_PROX_HYSTERESIS)) + return -EINVAL; + + mutex_lock(&chip->mutex); + chip->prox_thres = value; + + apds990x_force_p_refresh(chip); + mutex_unlock(&chip->mutex); + return len; +} + +static DEVICE_ATTR(prox0_thresh_above_value, S_IRUGO | S_IWUSR, + apds990x_prox_threshold_show, + apds990x_prox_threshold_store); + +static ssize_t apds990x_power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", !pm_runtime_suspended(dev)); + return 0; +} + +static ssize_t apds990x_power_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + unsigned long value; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + if (value) { + pm_runtime_get_sync(dev); + mutex_lock(&chip->mutex); + chip->lux_wait_fresh_res = true; + apds990x_force_a_refresh(chip); + apds990x_force_p_refresh(chip); + mutex_unlock(&chip->mutex); + } else { + if (!pm_runtime_suspended(dev)) + pm_runtime_put(dev); + } + return len; +} + +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, + apds990x_power_state_show, + apds990x_power_state_store); + +static ssize_t apds990x_chip_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct apds990x_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%s %d\n", chip->chipname, chip->revision); +} + +static DEVICE_ATTR(chip_id, S_IRUGO, apds990x_chip_id_show, NULL); + +static struct attribute *sysfs_attrs_ctrl[] = { + &dev_attr_lux0_calibscale.attr, + &dev_attr_lux0_calibscale_default.attr, + &dev_attr_lux0_input.attr, + &dev_attr_lux0_sensor_range.attr, + &dev_attr_lux0_rate.attr, + &dev_attr_lux0_rate_avail.attr, + &dev_attr_lux0_thresh_above_value.attr, + &dev_attr_lux0_thresh_below_value.attr, + &dev_attr_prox0_raw_en.attr, + &dev_attr_prox0_raw.attr, + &dev_attr_prox0_sensor_range.attr, + &dev_attr_prox0_thresh_above_value.attr, + &dev_attr_prox0_reporting_mode.attr, + &dev_attr_prox0_reporting_mode_avail.attr, + &dev_attr_chip_id.attr, + &dev_attr_power_state.attr, + NULL +}; + +static struct attribute_group apds990x_attribute_group[] = { + {.attrs = sysfs_attrs_ctrl }, +}; + +static int __devinit apds990x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct apds990x_chip *chip; + int err; + + chip = kzalloc(sizeof *chip, GFP_KERNEL); + if (!chip) + return -ENOMEM; + + i2c_set_clientdata(client, chip); + chip->client = client; + + init_waitqueue_head(&chip->wait); + mutex_init(&chip->mutex); + chip->pdata = client->dev.platform_data; + + if (chip->pdata == NULL) { + dev_err(&client->dev, "platform data is mandatory\n"); + err = -EINVAL; + goto fail1; + } + + if (chip->pdata->cf.ga == 0) { + /* set uncovered sensor default parameters */ + chip->cf.ga = 1966; /* 0.48 * APDS_PARAM_SCALE */ + chip->cf.cf1 = 4096; /* 1.00 * APDS_PARAM_SCALE */ + chip->cf.irf1 = 9134; /* 2.23 * APDS_PARAM_SCALE */ + chip->cf.cf2 = 2867; /* 0.70 * APDS_PARAM_SCALE */ + chip->cf.irf2 = 5816; /* 1.42 * APDS_PARAM_SCALE */ + chip->cf.df = 52; + } else { + chip->cf = chip->pdata->cf; + } + + /* precalculate inverse chip factors for threshold control */ + chip->rcf.afactor = + (chip->cf.irf1 - chip->cf.irf2) * APDS_PARAM_SCALE / + (chip->cf.cf1 - chip->cf.cf2); + chip->rcf.cf1 = APDS_PARAM_SCALE * APDS_PARAM_SCALE / + chip->cf.cf1; + chip->rcf.irf1 = chip->cf.irf1 * APDS_PARAM_SCALE / + chip->cf.cf1; + chip->rcf.cf2 = APDS_PARAM_SCALE * APDS_PARAM_SCALE / + chip->cf.cf2; + chip->rcf.irf2 = chip->cf.irf2 * APDS_PARAM_SCALE / + chip->cf.cf2; + + /* Set something to start with */ + chip->lux_thres_hi = APDS_LUX_DEF_THRES_HI; + chip->lux_thres_lo = APDS_LUX_DEF_THRES_LO; + chip->lux_calib = APDS_LUX_NEUTRAL_CALIB_VALUE; + + chip->prox_thres = APDS_PROX_DEF_THRES; + chip->pdrive = chip->pdata->pdrive; + chip->pdiode = APDS_PDIODE_IR; + chip->pgain = APDS_PGAIN_1X; + chip->prox_calib = APDS_PROX_NEUTRAL_CALIB_VALUE; + chip->prox_persistence = APDS_DEFAULT_PROX_PERS; + chip->prox_continuous_mode = false; + + chip->regs[0].supply = reg_vcc; + chip->regs[1].supply = reg_vled; + + err = regulator_bulk_get(&client->dev, + ARRAY_SIZE(chip->regs), chip->regs); + if (err < 0) { + dev_err(&client->dev, "Cannot get regulators\n"); + goto fail1; + } + + err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), chip->regs); + if (err < 0) { + dev_err(&client->dev, "Cannot enable regulators\n"); + goto fail2; + } + + usleep_range(APDS_STARTUP_DELAY, 2 * APDS_STARTUP_DELAY); + + err = apds990x_detect(chip); + if (err < 0) { + dev_err(&client->dev, "APDS990X not found\n"); + goto fail3; + } + + pm_runtime_set_active(&client->dev); + + apds990x_configure(chip); + apds990x_set_arate(chip, APDS_LUX_DEFAULT_RATE); + apds990x_mode_on(chip); + + pm_runtime_enable(&client->dev); + + if (chip->pdata->setup_resources) { + err = chip->pdata->setup_resources(); + if (err) { + err = -EINVAL; + goto fail3; + } + } + + err = sysfs_create_group(&chip->client->dev.kobj, + apds990x_attribute_group); + if (err < 0) { + dev_err(&chip->client->dev, "Sysfs registration failed\n"); + goto fail4; + } + + err = request_threaded_irq(client->irq, NULL, + apds990x_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW | + IRQF_ONESHOT, + "apds990x", chip); + if (err) { + dev_err(&client->dev, "could not get IRQ %d\n", + client->irq); + goto fail5; + } + return err; +fail5: + sysfs_remove_group(&chip->client->dev.kobj, + &apds990x_attribute_group[0]); +fail4: + if (chip->pdata && chip->pdata->release_resources) + chip->pdata->release_resources(); +fail3: + regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); +fail2: + regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs); +fail1: + kfree(chip); + return err; +} + +static int __devexit apds990x_remove(struct i2c_client *client) +{ + struct apds990x_chip *chip = i2c_get_clientdata(client); + + free_irq(client->irq, chip); + sysfs_remove_group(&chip->client->dev.kobj, + apds990x_attribute_group); + + if (chip->pdata && chip->pdata->release_resources) + chip->pdata->release_resources(); + + if (!pm_runtime_suspended(&client->dev)) + apds990x_chip_off(chip); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs); + + kfree(chip); + return 0; +} + +#ifdef CONFIG_PM +static int apds990x_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct apds990x_chip *chip = i2c_get_clientdata(client); + + apds990x_chip_off(chip); + return 0; +} + +static int apds990x_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct apds990x_chip *chip = i2c_get_clientdata(client); + + /* + * If we were enabled at suspend time, it is expected + * everything works nice and smoothly. Chip_on is enough + */ + apds990x_chip_on(chip); + + return 0; +} +#else +#define apds990x_suspend NULL +#define apds990x_resume NULL +#define apds990x_shutdown NULL +#endif + +#ifdef CONFIG_PM_RUNTIME +static int apds990x_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct apds990x_chip *chip = i2c_get_clientdata(client); + + apds990x_chip_off(chip); + return 0; +} + +static int apds990x_runtime_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct apds990x_chip *chip = i2c_get_clientdata(client); + + apds990x_chip_on(chip); + return 0; +} + +#endif + +static const struct i2c_device_id apds990x_id[] = { + {"apds990x", 0 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, apds990x_id); + +static const struct dev_pm_ops apds990x_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(apds990x_suspend, apds990x_resume) + SET_RUNTIME_PM_OPS(apds990x_runtime_suspend, + apds990x_runtime_resume, + NULL) +}; + +static struct i2c_driver apds990x_driver = { + .driver = { + .name = "apds990x", + .owner = THIS_MODULE, + .pm = &apds990x_pm_ops, + }, + .probe = apds990x_probe, + .remove = __devexit_p(apds990x_remove), + .id_table = apds990x_id, +}; + +static int __init apds990x_init(void) +{ + return i2c_add_driver(&apds990x_driver); +} + +static void __exit apds990x_exit(void) +{ + i2c_del_driver(&apds990x_driver); +} + +MODULE_DESCRIPTION("APDS990X combined ALS and proximity sensor"); +MODULE_AUTHOR("Samu Onkalo, Nokia Corporation"); +MODULE_LICENSE("GPL v2"); + +module_init(apds990x_init); +module_exit(apds990x_exit); diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c new file mode 100644 index 00000000000..cee632e645e --- /dev/null +++ b/drivers/misc/bh1770glc.c @@ -0,0 +1,1413 @@ +/* + * This file is part of the ROHM BH1770GLC / OSRAM SFH7770 sensor driver. + * Chip is combined proximity and ambient light sensor. + * + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/i2c/bh1770glc.h> +#include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h> +#include <linux/workqueue.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/slab.h> + +#define BH1770_ALS_CONTROL 0x80 /* ALS operation mode control */ +#define BH1770_PS_CONTROL 0x81 /* PS operation mode control */ +#define BH1770_I_LED 0x82 /* active LED and LED1, LED2 current */ +#define BH1770_I_LED3 0x83 /* LED3 current setting */ +#define BH1770_ALS_PS_MEAS 0x84 /* Forced mode trigger */ +#define BH1770_PS_MEAS_RATE 0x85 /* PS meas. rate at stand alone mode */ +#define BH1770_ALS_MEAS_RATE 0x86 /* ALS meas. rate at stand alone mode */ +#define BH1770_PART_ID 0x8a /* Part number and revision ID */ +#define BH1770_MANUFACT_ID 0x8b /* Manufacturerer ID */ +#define BH1770_ALS_DATA_0 0x8c /* ALS DATA low byte */ +#define BH1770_ALS_DATA_1 0x8d /* ALS DATA high byte */ +#define BH1770_ALS_PS_STATUS 0x8e /* Measurement data and int status */ +#define BH1770_PS_DATA_LED1 0x8f /* PS data from LED1 */ +#define BH1770_PS_DATA_LED2 0x90 /* PS data from LED2 */ +#define BH1770_PS_DATA_LED3 0x91 /* PS data from LED3 */ +#define BH1770_INTERRUPT 0x92 /* Interrupt setting */ +#define BH1770_PS_TH_LED1 0x93 /* PS interrupt threshold for LED1 */ +#define BH1770_PS_TH_LED2 0x94 /* PS interrupt threshold for LED2 */ +#define BH1770_PS_TH_LED3 0x95 /* PS interrupt threshold for LED3 */ +#define BH1770_ALS_TH_UP_0 0x96 /* ALS upper threshold low byte */ +#define BH1770_ALS_TH_UP_1 0x97 /* ALS upper threshold high byte */ +#define BH1770_ALS_TH_LOW_0 0x98 /* ALS lower threshold low byte */ +#define BH1770_ALS_TH_LOW_1 0x99 /* ALS lower threshold high byte */ + +/* MANUFACT_ID */ +#define BH1770_MANUFACT_ROHM 0x01 +#define BH1770_MANUFACT_OSRAM 0x03 + +/* PART_ID */ +#define BH1770_PART 0x90 +#define BH1770_PART_MASK 0xf0 +#define BH1770_REV_MASK 0x0f +#define BH1770_REV_SHIFT 0 +#define BH1770_REV_0 0x00 +#define BH1770_REV_1 0x01 + +/* Operating modes for both */ +#define BH1770_STANDBY 0x00 +#define BH1770_FORCED 0x02 +#define BH1770_STANDALONE 0x03 +#define BH1770_SWRESET (0x01 << 2) + +#define BH1770_PS_TRIG_MEAS (1 << 0) +#define BH1770_ALS_TRIG_MEAS (1 << 1) + +/* Interrupt control */ +#define BH1770_INT_OUTPUT_MODE (1 << 3) /* 0 = latched */ +#define BH1770_INT_POLARITY (1 << 2) /* 1 = active high */ +#define BH1770_INT_ALS_ENA (1 << 1) +#define BH1770_INT_PS_ENA (1 << 0) + +/* Interrupt status */ +#define BH1770_INT_LED1_DATA (1 << 0) +#define BH1770_INT_LED1_INT (1 << 1) +#define BH1770_INT_LED2_DATA (1 << 2) +#define BH1770_INT_LED2_INT (1 << 3) +#define BH1770_INT_LED3_DATA (1 << 4) +#define BH1770_INT_LED3_INT (1 << 5) +#define BH1770_INT_LEDS_INT ((1 << 1) | (1 << 3) | (1 << 5)) +#define BH1770_INT_ALS_DATA (1 << 6) +#define BH1770_INT_ALS_INT (1 << 7) + +/* Led channels */ +#define BH1770_LED1 0x00 + +#define BH1770_DISABLE 0 +#define BH1770_ENABLE 1 +#define BH1770_PROX_CHANNELS 1 + +#define BH1770_LUX_DEFAULT_RATE 1 /* Index to lux rate table */ +#define BH1770_PROX_DEFAULT_RATE 1 /* Direct HW value =~ 50Hz */ +#define BH1770_PROX_DEF_RATE_THRESH 6 /* Direct HW value =~ 5 Hz */ +#define BH1770_STARTUP_DELAY 50 +#define BH1770_RESET_TIME 10 +#define BH1770_TIMEOUT 2100 /* Timeout in 2.1 seconds */ + +#define BH1770_LUX_RANGE 65535 +#define BH1770_PROX_RANGE 255 +#define BH1770_COEF_SCALER 1024 +#define BH1770_CALIB_SCALER 8192 +#define BH1770_LUX_NEUTRAL_CALIB_VALUE (1 * BH1770_CALIB_SCALER) +#define BH1770_LUX_DEF_THRES 1000 +#define BH1770_PROX_DEF_THRES 70 +#define BH1770_PROX_DEF_ABS_THRES 100 +#define BH1770_DEFAULT_PERSISTENCE 10 +#define BH1770_PROX_MAX_PERSISTENCE 50 +#define BH1770_LUX_GA_SCALE 16384 +#define BH1770_LUX_CF_SCALE 2048 /* CF ChipFactor */ +#define BH1770_NEUTRAL_CF BH1770_LUX_CF_SCALE +#define BH1770_LUX_CORR_SCALE 4096 + +#define PROX_ABOVE_THRESHOLD 1 +#define PROX_BELOW_THRESHOLD 0 + +#define PROX_IGNORE_LUX_LIMIT 500 + +struct bh1770_chip { + struct bh1770_platform_data *pdata; + char chipname[10]; + u8 revision; + struct i2c_client *client; + struct regulator_bulk_data regs[2]; + struct mutex mutex; /* avoid parallel access */ + wait_queue_head_t wait; + + bool int_mode_prox; + bool int_mode_lux; + struct delayed_work prox_work; + u32 lux_cf; /* Chip specific factor */ + u32 lux_ga; + u32 lux_calib; + int lux_rate_index; + u32 lux_corr; + u16 lux_data_raw; + u16 lux_threshold_hi; + u16 lux_threshold_lo; + u16 lux_thres_hi_onchip; + u16 lux_thres_lo_onchip; + bool lux_wait_result; + + int prox_enable_count; + u16 prox_coef; + u16 prox_const; + int prox_rate; + int prox_rate_threshold; + u8 prox_persistence; + u8 prox_persistence_counter; + u8 prox_data; + u8 prox_threshold; + u8 prox_threshold_hw; + bool prox_force_update; + u8 prox_abs_thres; + u8 prox_led; +}; + +static const char reg_vcc[] = "Vcc"; +static const char reg_vleds[] = "Vleds"; + +/* + * Supported stand alone rates in ms from chip data sheet + * {10, 20, 30, 40, 70, 100, 200, 500, 1000, 2000}; + */ +static const s16 prox_rates_hz[] = {100, 50, 33, 25, 14, 10, 5, 2}; +static const s16 prox_rates_ms[] = {10, 20, 30, 40, 70, 100, 200, 500}; + +/* Supported IR-led currents in mA */ +static const u8 prox_curr_ma[] = {5, 10, 20, 50, 100, 150, 200}; + +/* + * Supported stand alone rates in ms from chip data sheet + * {100, 200, 500, 1000, 2000}; + */ +static const s16 lux_rates_hz[] = {10, 5, 2, 1, 0}; + +/* + * interrupt control functions are called while keeping chip->mutex + * excluding module probe / remove + */ +static inline int bh1770_lux_interrupt_control(struct bh1770_chip *chip, + int lux) +{ + chip->int_mode_lux = lux; + /* Set interrupt modes, interrupt active low, latched */ + return i2c_smbus_write_byte_data(chip->client, + BH1770_INTERRUPT, + (lux << 1) | chip->int_mode_prox); +} + +static inline int bh1770_prox_interrupt_control(struct bh1770_chip *chip, + int ps) +{ + chip->int_mode_prox = ps; + return i2c_smbus_write_byte_data(chip->client, + BH1770_INTERRUPT, + (chip->int_mode_lux << 1) | (ps << 0)); +} + +/* chip->mutex is always kept here */ +static int bh1770_lux_rate(struct bh1770_chip *chip, int rate_index) +{ + /* sysfs may call this when the chip is powered off */ + if (pm_runtime_suspended(&chip->client->dev)) + return 0; + + /* Proper proximity response needs fastest lux rate (100ms) */ + if (chip->prox_enable_count) + rate_index = 0; + + return i2c_smbus_write_byte_data(chip->client, + BH1770_ALS_MEAS_RATE, + rate_index); +} + +static int bh1770_prox_rate(struct bh1770_chip *chip, int mode) +{ + int rate; + + rate = (mode == PROX_ABOVE_THRESHOLD) ? + chip->prox_rate_threshold : chip->prox_rate; + + return i2c_smbus_write_byte_data(chip->client, + BH1770_PS_MEAS_RATE, + rate); +} + +/* InfraredLED is controlled by the chip during proximity scanning */ +static inline int bh1770_led_cfg(struct bh1770_chip *chip) +{ + /* LED cfg, current for leds 1 and 2 */ + return i2c_smbus_write_byte_data(chip->client, + BH1770_I_LED, + (BH1770_LED1 << 6) | + (BH1770_LED_5mA << 3) | + chip->prox_led); +} + +/* + * Following two functions converts raw ps values from HW to normalized + * values. Purpose is to compensate differences between different sensor + * versions and variants so that result means about the same between + * versions. + */ +static inline u8 bh1770_psraw_to_adjusted(struct bh1770_chip *chip, u8 psraw) +{ + u16 adjusted; + adjusted = (u16)(((u32)(psraw + chip->prox_const) * chip->prox_coef) / + BH1770_COEF_SCALER); + if (adjusted > BH1770_PROX_RANGE) + adjusted = BH1770_PROX_RANGE; + return adjusted; +} + +static inline u8 bh1770_psadjusted_to_raw(struct bh1770_chip *chip, u8 ps) +{ + u16 raw; + + raw = (((u32)ps * BH1770_COEF_SCALER) / chip->prox_coef); + if (raw > chip->prox_const) + raw = raw - chip->prox_const; + else + raw = 0; + return raw; +} + +/* + * Following two functions converts raw lux values from HW to normalized + * values. Purpose is to compensate differences between different sensor + * versions and variants so that result means about the same between + * versions. Chip->mutex is kept when this is called. + */ +static int bh1770_prox_set_threshold(struct bh1770_chip *chip) +{ + u8 tmp = 0; + + /* sysfs may call this when the chip is powered off */ + if (pm_runtime_suspended(&chip->client->dev)) + return 0; + + tmp = bh1770_psadjusted_to_raw(chip, chip->prox_threshold); + chip->prox_threshold_hw = tmp; + + return i2c_smbus_write_byte_data(chip->client, BH1770_PS_TH_LED1, + tmp); +} + +static inline u16 bh1770_lux_raw_to_adjusted(struct bh1770_chip *chip, u16 raw) +{ + u32 lux; + lux = ((u32)raw * chip->lux_corr) / BH1770_LUX_CORR_SCALE; + return min(lux, (u32)BH1770_LUX_RANGE); +} + +static inline u16 bh1770_lux_adjusted_to_raw(struct bh1770_chip *chip, + u16 adjusted) +{ + return (u32)adjusted * BH1770_LUX_CORR_SCALE / chip->lux_corr; +} + +/* chip->mutex is kept when this is called */ +static int bh1770_lux_update_thresholds(struct bh1770_chip *chip, + u16 threshold_hi, u16 threshold_lo) +{ + u8 data[4]; + int ret; + + /* sysfs may call this when the chip is powered off */ + if (pm_runtime_suspended(&chip->client->dev)) + return 0; + + /* + * Compensate threshold values with the correction factors if not + * set to minimum or maximum. + * Min & max values disables interrupts. + */ + if (threshold_hi != BH1770_LUX_RANGE && threshold_hi != 0) + threshold_hi = bh1770_lux_adjusted_to_raw(chip, threshold_hi); + + if (threshold_lo != BH1770_LUX_RANGE && threshold_lo != 0) + threshold_lo = bh1770_lux_adjusted_to_raw(chip, threshold_lo); + + if (chip->lux_thres_hi_onchip == threshold_hi && + chip->lux_thres_lo_onchip == threshold_lo) + return 0; + + chip->lux_thres_hi_onchip = threshold_hi; + chip->lux_thres_lo_onchip = threshold_lo; + + data[0] = threshold_hi; + data[1] = threshold_hi >> 8; + data[2] = threshold_lo; + data[3] = threshold_lo >> 8; + + ret = i2c_smbus_write_i2c_block_data(chip->client, + BH1770_ALS_TH_UP_0, + ARRAY_SIZE(data), + data); + return ret; +} + +static int bh1770_lux_get_result(struct bh1770_chip *chip) +{ + u16 data; + int ret; + + ret = i2c_smbus_read_byte_data(chip->client, BH1770_ALS_DATA_0); + if (ret < 0) + return ret; + + data = ret & 0xff; + ret = i2c_smbus_read_byte_data(chip->client, BH1770_ALS_DATA_1); + if (ret < 0) + return ret; + + chip->lux_data_raw = data | ((ret & 0xff) << 8); + + return 0; +} + +/* Calculate correction value which contains chip and device specific parts */ +static u32 bh1770_get_corr_value(struct bh1770_chip *chip) +{ + u32 tmp; + /* Impact of glass attenuation correction */ + tmp = (BH1770_LUX_CORR_SCALE * chip->lux_ga) / BH1770_LUX_GA_SCALE; + /* Impact of chip factor correction */ + tmp = (tmp * chip->lux_cf) / BH1770_LUX_CF_SCALE; + /* Impact of Device specific calibration correction */ + tmp = (tmp * chip->lux_calib) / BH1770_CALIB_SCALER; + return tmp; +} + +static int bh1770_lux_read_result(struct bh1770_chip *chip) +{ + bh1770_lux_get_result(chip); + return bh1770_lux_raw_to_adjusted(chip, chip->lux_data_raw); +} + +/* + * Chip on / off functions are called while keeping mutex except probe + * or remove phase + */ +static int bh1770_chip_on(struct bh1770_chip *chip) +{ + int ret = regulator_bulk_enable(ARRAY_SIZE(chip->regs), + chip->regs); + if (ret < 0) + return ret; + + usleep_range(BH1770_STARTUP_DELAY, BH1770_STARTUP_DELAY * 2); + + /* Reset the chip */ + i2c_smbus_write_byte_data(chip->client, BH1770_ALS_CONTROL, + BH1770_SWRESET); + usleep_range(BH1770_RESET_TIME, BH1770_RESET_TIME * 2); + + /* + * ALS is started always since proximity needs als results + * for realibility estimation. + * Let's assume dark until the first ALS measurement is ready. + */ + chip->lux_data_raw = 0; + chip->prox_data = 0; + ret = i2c_smbus_write_byte_data(chip->client, + BH1770_ALS_CONTROL, BH1770_STANDALONE); + + /* Assume reset defaults */ + chip->lux_thres_hi_onchip = BH1770_LUX_RANGE; + chip->lux_thres_lo_onchip = 0; + + return ret; +} + +static void bh1770_chip_off(struct bh1770_chip *chip) +{ + i2c_smbus_write_byte_data(chip->client, + BH1770_INTERRUPT, BH1770_DISABLE); + i2c_smbus_write_byte_data(chip->client, + BH1770_ALS_CONTROL, BH1770_STANDBY); + i2c_smbus_write_byte_data(chip->client, + BH1770_PS_CONTROL, BH1770_STANDBY); + regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); +} + +/* chip->mutex is kept when this is called */ +static int bh1770_prox_mode_control(struct bh1770_chip *chip) +{ + if (chip->prox_enable_count) { + chip->prox_force_update = true; /* Force immediate update */ + + bh1770_lux_rate(chip, chip->lux_rate_index); + bh1770_prox_set_threshold(chip); + bh1770_led_cfg(chip); + bh1770_prox_rate(chip, PROX_BELOW_THRESHOLD); + bh1770_prox_interrupt_control(chip, BH1770_ENABLE); + i2c_smbus_write_byte_data(chip->client, + BH1770_PS_CONTROL, BH1770_STANDALONE); + } else { + chip->prox_data = 0; + bh1770_lux_rate(chip, chip->lux_rate_index); + bh1770_prox_interrupt_control(chip, BH1770_DISABLE); + i2c_smbus_write_byte_data(chip->client, + BH1770_PS_CONTROL, BH1770_STANDBY); + } + return 0; +} + +/* chip->mutex is kept when this is called */ +static int bh1770_prox_read_result(struct bh1770_chip *chip) +{ + int ret; + bool above; + u8 mode; + + ret = i2c_smbus_read_byte_data(chip->client, BH1770_PS_DATA_LED1); + if (ret < 0) + goto out; + + if (ret > chip->prox_threshold_hw) + above = true; + else + above = false; + + /* + * when ALS levels goes above limit, proximity result may be + * false proximity. Thus ignore the result. With real proximity + * there is a shadow causing low als levels. + */ + if (chip->lux_data_raw > PROX_IGNORE_LUX_LIMIT) + ret = 0; + + chip->prox_data = bh1770_psraw_to_adjusted(chip, ret); + + /* Strong proximity level or force mode requires immediate response */ + if (chip->prox_data >= chip->prox_abs_thres || + chip->prox_force_update) + chip->prox_persistence_counter = chip->prox_persistence; + + chip->prox_force_update = false; + + /* Persistence filttering to reduce false proximity events */ + if (likely(above)) { + if (chip->prox_persistence_counter < chip->prox_persistence) { + chip->prox_persistence_counter++; + ret = -ENODATA; + } else { + mode = PROX_ABOVE_THRESHOLD; + ret = 0; + } + } else { + chip->prox_persistence_counter = 0; + mode = PROX_BELOW_THRESHOLD; + chip->prox_data = 0; + ret = 0; + } + + /* Set proximity detection rate based on above or below value */ + if (ret == 0) { + bh1770_prox_rate(chip, mode); + sysfs_notify(&chip->client->dev.kobj, NULL, "prox0_raw"); + } +out: + return ret; +} + +static int bh1770_detect(struct bh1770_chip *chip) +{ + struct i2c_client *client = chip->client; + s32 ret; + u8 manu, part; + + ret = i2c_smbus_read_byte_data(client, BH1770_MANUFACT_ID); + if (ret < 0) + goto error; + manu = (u8)ret; + + ret = i2c_smbus_read_byte_data(client, BH1770_PART_ID); + if (ret < 0) + goto error; + part = (u8)ret; + + chip->revision = (part & BH1770_REV_MASK) >> BH1770_REV_SHIFT; + chip->prox_coef = BH1770_COEF_SCALER; + chip->prox_const = 0; + chip->lux_cf = BH1770_NEUTRAL_CF; + + if ((manu == BH1770_MANUFACT_ROHM) && + ((part & BH1770_PART_MASK) == BH1770_PART)) { + snprintf(chip->chipname, sizeof(chip->chipname), "BH1770GLC"); + return 0; + } + + if ((manu == BH1770_MANUFACT_OSRAM) && + ((part & BH1770_PART_MASK) == BH1770_PART)) { + snprintf(chip->chipname, sizeof(chip->chipname), "SFH7770"); + /* Values selected by comparing different versions */ + chip->prox_coef = 819; /* 0.8 * BH1770_COEF_SCALER */ + chip->prox_const = 40; + return 0; + } + + ret = -ENODEV; +error: + dev_dbg(&client->dev, "BH1770 or SFH7770 not found\n"); + + return ret; +} + +/* + * This work is re-scheduled at every proximity interrupt. + * If this work is running, it means that there hasn't been any + * proximity interrupt in time. Situation is handled as no-proximity. + * It would be nice to have low-threshold interrupt or interrupt + * when measurement and hi-threshold are both 0. But neither of those exists. + * This is a workaroud for missing HW feature. + */ + +static void bh1770_prox_work(struct work_struct *work) +{ + struct bh1770_chip *chip = + container_of(work, struct bh1770_chip, prox_work.work); + + mutex_lock(&chip->mutex); + bh1770_prox_read_result(chip); + mutex_unlock(&chip->mutex); +} + +/* This is threaded irq handler */ +static irqreturn_t bh1770_irq(int irq, void *data) +{ + struct bh1770_chip *chip = data; + int status; + int rate = 0; + + mutex_lock(&chip->mutex); + status = i2c_smbus_read_byte_data(chip->client, BH1770_ALS_PS_STATUS); + + /* Acknowledge interrupt by reading this register */ + i2c_smbus_read_byte_data(chip->client, BH1770_INTERRUPT); + + /* + * Check if there is fresh data available for als. + * If this is the very first data, update thresholds after that. + */ + if (status & BH1770_INT_ALS_DATA) { + bh1770_lux_get_result(chip); + if (unlikely(chip->lux_wait_result)) { + chip->lux_wait_result = false; + wake_up(&chip->wait); + bh1770_lux_update_thresholds(chip, + chip->lux_threshold_hi, + chip->lux_threshold_lo); + } + } + + /* Disable interrupt logic to guarantee acknowledgement */ + i2c_smbus_write_byte_data(chip->client, BH1770_INTERRUPT, + (0 << 1) | (0 << 0)); + + if ((status & BH1770_INT_ALS_INT)) + sysfs_notify(&chip->client->dev.kobj, NULL, "lux0_input"); + + if (chip->int_mode_prox && (status & BH1770_INT_LEDS_INT)) { + rate = prox_rates_ms[chip->prox_rate_threshold]; + bh1770_prox_read_result(chip); + } + + /* Re-enable interrupt logic */ + i2c_smbus_write_byte_data(chip->client, BH1770_INTERRUPT, + (chip->int_mode_lux << 1) | + (chip->int_mode_prox << 0)); + mutex_unlock(&chip->mutex); + + /* + * Can't cancel work while keeping mutex since the work uses the + * same mutex. + */ + if (rate) { + /* + * Simulate missing no-proximity interrupt 50ms after the + * next expected interrupt time. + */ + cancel_delayed_work_sync(&chip->prox_work); + schedule_delayed_work(&chip->prox_work, + msecs_to_jiffies(rate + 50)); + } + return IRQ_HANDLED; +} + +static ssize_t bh1770_power_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + unsigned long value; + size_t ret; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + + mutex_lock(&chip->mutex); + if (value) { + pm_runtime_get_sync(dev); + + ret = bh1770_lux_rate(chip, chip->lux_rate_index); + ret |= bh1770_lux_interrupt_control(chip, BH1770_ENABLE); + + if (ret < 0) { + pm_runtime_put(dev); + goto leave; + } + + /* This causes interrupt after the next measurement cycle */ + bh1770_lux_update_thresholds(chip, BH1770_LUX_DEF_THRES, + BH1770_LUX_DEF_THRES); + /* Inform that we are waiting for a result from ALS */ + chip->lux_wait_result = true; + bh1770_prox_mode_control(chip); + } else if (!pm_runtime_suspended(dev)) { + pm_runtime_put(dev); + } + ret = count; +leave: + mutex_unlock(&chip->mutex); + return ret; +} + +static ssize_t bh1770_power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", !pm_runtime_suspended(dev)); +} + +static ssize_t bh1770_lux_result_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + ssize_t ret; + long timeout; + + if (pm_runtime_suspended(dev)) + return -EIO; /* Chip is not enabled at all */ + + timeout = wait_event_interruptible_timeout(chip->wait, + !chip->lux_wait_result, + msecs_to_jiffies(BH1770_TIMEOUT)); + if (!timeout) + return -EIO; + + mutex_lock(&chip->mutex); + ret = sprintf(buf, "%d\n", bh1770_lux_read_result(chip)); + mutex_unlock(&chip->mutex); + + return ret; +} + +static ssize_t bh1770_lux_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", BH1770_LUX_RANGE); +} + +static ssize_t bh1770_prox_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + unsigned long value; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + + mutex_lock(&chip->mutex); + /* Assume no proximity. Sensor will tell real state soon */ + if (!chip->prox_enable_count) + chip->prox_data = 0; + + if (value) + chip->prox_enable_count++; + else if (chip->prox_enable_count > 0) + chip->prox_enable_count--; + else + goto leave; + + /* Run control only when chip is powered on */ + if (!pm_runtime_suspended(dev)) + bh1770_prox_mode_control(chip); +leave: + mutex_unlock(&chip->mutex); + return count; +} + +static ssize_t bh1770_prox_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + ssize_t len; + + mutex_lock(&chip->mutex); + len = sprintf(buf, "%d\n", chip->prox_enable_count); + mutex_unlock(&chip->mutex); + return len; +} + +static ssize_t bh1770_prox_result_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&chip->mutex); + if (chip->prox_enable_count && !pm_runtime_suspended(dev)) + ret = sprintf(buf, "%d\n", chip->prox_data); + else + ret = -EIO; + mutex_unlock(&chip->mutex); + return ret; +} + +static ssize_t bh1770_prox_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", BH1770_PROX_RANGE); +} + +static ssize_t bh1770_get_prox_rate_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + int pos = 0; + for (i = 0; i < ARRAY_SIZE(prox_rates_hz); i++) + pos += sprintf(buf + pos, "%d ", prox_rates_hz[i]); + sprintf(buf + pos - 1, "\n"); + return pos; +} + +static ssize_t bh1770_get_prox_rate_above(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", prox_rates_hz[chip->prox_rate_threshold]); +} + +static ssize_t bh1770_get_prox_rate_below(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", prox_rates_hz[chip->prox_rate]); +} + +static int bh1770_prox_rate_validate(int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(prox_rates_hz) - 1; i++) + if (rate >= prox_rates_hz[i]) + break; + return i; +} + +static ssize_t bh1770_set_prox_rate_above(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + unsigned long value; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + + mutex_lock(&chip->mutex); + chip->prox_rate_threshold = bh1770_prox_rate_validate(value); + mutex_unlock(&chip->mutex); + return count; +} + +static ssize_t bh1770_set_prox_rate_below(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + unsigned long value; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + + mutex_lock(&chip->mutex); + chip->prox_rate = bh1770_prox_rate_validate(value); + mutex_unlock(&chip->mutex); + return count; +} + +static ssize_t bh1770_get_prox_thres(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", chip->prox_threshold); +} + +static ssize_t bh1770_set_prox_thres(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + unsigned long value; + int ret; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + if (value > BH1770_PROX_RANGE) + return -EINVAL; + + mutex_lock(&chip->mutex); + chip->prox_threshold = value; + ret = bh1770_prox_set_threshold(chip); + mutex_unlock(&chip->mutex); + if (ret < 0) + return ret; + return count; +} + +static ssize_t bh1770_prox_persistence_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", chip->prox_persistence); +} + +static ssize_t bh1770_prox_persistence_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + unsigned long value; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + + if (value > BH1770_PROX_MAX_PERSISTENCE) + return -EINVAL; + + chip->prox_persistence = value; + + return len; +} + +static ssize_t bh1770_prox_abs_thres_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%u\n", chip->prox_abs_thres); +} + +static ssize_t bh1770_prox_abs_thres_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + unsigned long value; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + + if (value > BH1770_PROX_RANGE) + return -EINVAL; + + chip->prox_abs_thres = value; + + return len; +} + +static ssize_t bh1770_chip_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%s rev %d\n", chip->chipname, chip->revision); +} + +static ssize_t bh1770_lux_calib_default_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", BH1770_CALIB_SCALER); +} + +static ssize_t bh1770_lux_calib_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + ssize_t len; + + mutex_lock(&chip->mutex); + len = sprintf(buf, "%u\n", chip->lux_calib); + mutex_unlock(&chip->mutex); + return len; +} + +static ssize_t bh1770_lux_calib_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + unsigned long value; + u32 old_calib; + u32 new_corr; + + if (strict_strtoul(buf, 0, &value)) + return -EINVAL; + + mutex_lock(&chip->mutex); + old_calib = chip->lux_calib; + chip->lux_calib = value; + new_corr = bh1770_get_corr_value(chip); + if (new_corr == 0) { + chip->lux_calib = old_calib; + mutex_unlock(&chip->mutex); + return -EINVAL; + } + chip->lux_corr = new_corr; + /* Refresh thresholds on HW after changing correction value */ + bh1770_lux_update_thresholds(chip, chip->lux_threshold_hi, + chip->lux_threshold_lo); + + mutex_unlock(&chip->mutex); + + return len; +} + +static ssize_t bh1770_get_lux_rate_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + int pos = 0; + for (i = 0; i < ARRAY_SIZE(lux_rates_hz); i++) + pos += sprintf(buf + pos, "%d ", lux_rates_hz[i]); + sprintf(buf + pos - 1, "\n"); + return pos; +} + +static ssize_t bh1770_get_lux_rate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", lux_rates_hz[chip->lux_rate_index]); +} + +static ssize_t bh1770_set_lux_rate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + unsigned long rate_hz; + int ret, i; + + if (strict_strtoul(buf, 0, &rate_hz)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(lux_rates_hz) - 1; i++) + if (rate_hz >= lux_rates_hz[i]) + break; + + mutex_lock(&chip->mutex); + chip->lux_rate_index = i; + ret = bh1770_lux_rate(chip, i); + mutex_unlock(&chip->mutex); + + if (ret < 0) + return ret; + + return count; +} + +static ssize_t bh1770_get_lux_thresh_above(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", chip->lux_threshold_hi); +} + +static ssize_t bh1770_get_lux_thresh_below(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", chip->lux_threshold_lo); +} + +static ssize_t bh1770_set_lux_thresh(struct bh1770_chip *chip, u16 *target, + const char *buf) +{ + int ret = 0; + unsigned long thresh; + + if (strict_strtoul(buf, 0, &thresh)) + return -EINVAL; + + if (thresh > BH1770_LUX_RANGE) + return -EINVAL; + + mutex_lock(&chip->mutex); + *target = thresh; + /* + * Don't update values in HW if we are still waiting for + * first interrupt to come after device handle open call. + */ + if (!chip->lux_wait_result) + ret = bh1770_lux_update_thresholds(chip, + chip->lux_threshold_hi, + chip->lux_threshold_lo); + mutex_unlock(&chip->mutex); + return ret; + +} + +static ssize_t bh1770_set_lux_thresh_above(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + int ret = bh1770_set_lux_thresh(chip, &chip->lux_threshold_hi, buf); + if (ret < 0) + return ret; + return len; +} + +static ssize_t bh1770_set_lux_thresh_below(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct bh1770_chip *chip = dev_get_drvdata(dev); + int ret = bh1770_set_lux_thresh(chip, &chip->lux_threshold_lo, buf); + if (ret < 0) + return ret; + return len; +} + +static DEVICE_ATTR(prox0_raw_en, S_IRUGO | S_IWUSR, bh1770_prox_enable_show, + bh1770_prox_enable_store); +static DEVICE_ATTR(prox0_thresh_above1_value, S_IRUGO | S_IWUSR, + bh1770_prox_abs_thres_show, + bh1770_prox_abs_thres_store); +static DEVICE_ATTR(prox0_thresh_above0_value, S_IRUGO | S_IWUSR, + bh1770_get_prox_thres, + bh1770_set_prox_thres); +static DEVICE_ATTR(prox0_raw, S_IRUGO, bh1770_prox_result_show, NULL); +static DEVICE_ATTR(prox0_sensor_range, S_IRUGO, bh1770_prox_range_show, NULL); +static DEVICE_ATTR(prox0_thresh_above_count, S_IRUGO | S_IWUSR, + bh1770_prox_persistence_show, + bh1770_prox_persistence_store); +static DEVICE_ATTR(prox0_rate_above, S_IRUGO | S_IWUSR, + bh1770_get_prox_rate_above, + bh1770_set_prox_rate_above); +static DEVICE_ATTR(prox0_rate_below, S_IRUGO | S_IWUSR, + bh1770_get_prox_rate_below, + bh1770_set_prox_rate_below); +static DEVICE_ATTR(prox0_rate_avail, S_IRUGO, bh1770_get_prox_rate_avail, NULL); + +static DEVICE_ATTR(lux0_calibscale, S_IRUGO | S_IWUSR, bh1770_lux_calib_show, + bh1770_lux_calib_store); +static DEVICE_ATTR(lux0_calibscale_default, S_IRUGO, + bh1770_lux_calib_default_show, + NULL); +static DEVICE_ATTR(lux0_input, S_IRUGO, bh1770_lux_result_show, NULL); +static DEVICE_ATTR(lux0_sensor_range, S_IRUGO, bh1770_lux_range_show, NULL); +static DEVICE_ATTR(lux0_rate, S_IRUGO | S_IWUSR, bh1770_get_lux_rate, + bh1770_set_lux_rate); +static DEVICE_ATTR(lux0_rate_avail, S_IRUGO, bh1770_get_lux_rate_avail, NULL); +static DEVICE_ATTR(lux0_thresh_above_value, S_IRUGO | S_IWUSR, + bh1770_get_lux_thresh_above, + bh1770_set_lux_thresh_above); +static DEVICE_ATTR(lux0_thresh_below_value, S_IRUGO | S_IWUSR, + bh1770_get_lux_thresh_below, + bh1770_set_lux_thresh_below); +static DEVICE_ATTR(chip_id, S_IRUGO, bh1770_chip_id_show, NULL); +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, bh1770_power_state_show, + bh1770_power_state_store); + + +static struct attribute *sysfs_attrs[] = { + &dev_attr_lux0_calibscale.attr, + &dev_attr_lux0_calibscale_default.attr, + &dev_attr_lux0_input.attr, + &dev_attr_lux0_sensor_range.attr, + &dev_attr_lux0_rate.attr, + &dev_attr_lux0_rate_avail.attr, + &dev_attr_lux0_thresh_above_value.attr, + &dev_attr_lux0_thresh_below_value.attr, + &dev_attr_prox0_raw.attr, + &dev_attr_prox0_sensor_range.attr, + &dev_attr_prox0_raw_en.attr, + &dev_attr_prox0_thresh_above_count.attr, + &dev_attr_prox0_rate_above.attr, + &dev_attr_prox0_rate_below.attr, + &dev_attr_prox0_rate_avail.attr, + &dev_attr_prox0_thresh_above0_value.attr, + &dev_attr_prox0_thresh_above1_value.attr, + &dev_attr_chip_id.attr, + &dev_attr_power_state.attr, + NULL +}; + +static struct attribute_group bh1770_attribute_group = { + .attrs = sysfs_attrs +}; + +static int __devinit bh1770_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bh1770_chip *chip; + int err; + + chip = kzalloc(sizeof *chip, GFP_KERNEL); + if (!chip) + return -ENOMEM; + + i2c_set_clientdata(client, chip); + chip->client = client; + + mutex_init(&chip->mutex); + init_waitqueue_head(&chip->wait); + INIT_DELAYED_WORK(&chip->prox_work, bh1770_prox_work); + + if (client->dev.platform_data == NULL) { + dev_err(&client->dev, "platform data is mandatory\n"); + err = -EINVAL; + goto fail1; + } + + chip->pdata = client->dev.platform_data; + chip->lux_calib = BH1770_LUX_NEUTRAL_CALIB_VALUE; + chip->lux_rate_index = BH1770_LUX_DEFAULT_RATE; + chip->lux_threshold_lo = BH1770_LUX_DEF_THRES; + chip->lux_threshold_hi = BH1770_LUX_DEF_THRES; + + if (chip->pdata->glass_attenuation == 0) + chip->lux_ga = BH1770_NEUTRAL_GA; + else + chip->lux_ga = chip->pdata->glass_attenuation; + + chip->prox_threshold = BH1770_PROX_DEF_THRES; + chip->prox_led = chip->pdata->led_def_curr; + chip->prox_abs_thres = BH1770_PROX_DEF_ABS_THRES; + chip->prox_persistence = BH1770_DEFAULT_PERSISTENCE; + chip->prox_rate_threshold = BH1770_PROX_DEF_RATE_THRESH; + chip->prox_rate = BH1770_PROX_DEFAULT_RATE; + chip->prox_data = 0; + + chip->regs[0].supply = reg_vcc; + chip->regs[1].supply = reg_vleds; + + err = regulator_bulk_get(&client->dev, + ARRAY_SIZE(chip->regs), chip->regs); + if (err < 0) { + dev_err(&client->dev, "Cannot get regulators\n"); + goto fail1; + } + + err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), + chip->regs); + if (err < 0) { + dev_err(&client->dev, "Cannot enable regulators\n"); + goto fail2; + } + + usleep_range(BH1770_STARTUP_DELAY, BH1770_STARTUP_DELAY * 2); + err = bh1770_detect(chip); + if (err < 0) + goto fail3; + + /* Start chip */ + bh1770_chip_on(chip); + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + + chip->lux_corr = bh1770_get_corr_value(chip); + if (chip->lux_corr == 0) { + dev_err(&client->dev, "Improper correction values\n"); + err = -EINVAL; + goto fail3; + } + + if (chip->pdata->setup_resources) { + err = chip->pdata->setup_resources(); + if (err) { + err = -EINVAL; + goto fail3; + } + } + + err = sysfs_create_group(&chip->client->dev.kobj, + &bh1770_attribute_group); + if (err < 0) { + dev_err(&chip->client->dev, "Sysfs registration failed\n"); + goto fail4; + } + + /* + * Chip needs level triggered interrupt to work. However, + * level triggering doesn't work always correctly with power + * management. Select both + */ + err = request_threaded_irq(client->irq, NULL, + bh1770_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT | + IRQF_TRIGGER_LOW, + "bh1770", chip); + if (err) { + dev_err(&client->dev, "could not get IRQ %d\n", + client->irq); + goto fail5; + } + regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); + return err; +fail5: + sysfs_remove_group(&chip->client->dev.kobj, + &bh1770_attribute_group); +fail4: + if (chip->pdata->release_resources) + chip->pdata->release_resources(); +fail3: + regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); +fail2: + regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs); +fail1: + kfree(chip); + return err; +} + +static int __devexit bh1770_remove(struct i2c_client *client) +{ + struct bh1770_chip *chip = i2c_get_clientdata(client); + + free_irq(client->irq, chip); + + sysfs_remove_group(&chip->client->dev.kobj, + &bh1770_attribute_group); + + if (chip->pdata->release_resources) + chip->pdata->release_resources(); + + cancel_delayed_work_sync(&chip->prox_work); + + if (!pm_runtime_suspended(&client->dev)) + bh1770_chip_off(chip); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs); + kfree(chip); + return 0; +} + +#ifdef CONFIG_PM +static int bh1770_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct bh1770_chip *chip = i2c_get_clientdata(client); + + bh1770_chip_off(chip); + + return 0; +} + +static int bh1770_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct bh1770_chip *chip = i2c_get_clientdata(client); + int ret = 0; + + bh1770_chip_on(chip); + + if (!pm_runtime_suspended(dev)) { + /* + * If we were enabled at suspend time, it is expected + * everything works nice and smoothly + */ + ret = bh1770_lux_rate(chip, chip->lux_rate_index); + ret |= bh1770_lux_interrupt_control(chip, BH1770_ENABLE); + + /* This causes interrupt after the next measurement cycle */ + bh1770_lux_update_thresholds(chip, BH1770_LUX_DEF_THRES, + BH1770_LUX_DEF_THRES); + /* Inform that we are waiting for a result from ALS */ + chip->lux_wait_result = true; + bh1770_prox_mode_control(chip); + } + return ret; +} + +#else +#define bh1770_suspend NULL +#define bh1770_shutdown NULL +#define bh1770_resume NULL +#endif + +#ifdef CONFIG_PM_RUNTIME +static int bh1770_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct bh1770_chip *chip = i2c_get_clientdata(client); + + bh1770_chip_off(chip); + + return 0; +} + +static int bh1770_runtime_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct bh1770_chip *chip = i2c_get_clientdata(client); + + bh1770_chip_on(chip); + + return 0; +} +#endif + +static const struct i2c_device_id bh1770_id[] = { + {"bh1770glc", 0 }, + {"sfh7770", 0 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, bh1770_id); + +static const struct dev_pm_ops bh1770_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(bh1770_suspend, bh1770_resume) + SET_RUNTIME_PM_OPS(bh1770_runtime_suspend, bh1770_runtime_resume, NULL) +}; + +static struct i2c_driver bh1770_driver = { + .driver = { + .name = "bh1770glc", + .owner = THIS_MODULE, + .pm = &bh1770_pm_ops, + }, + .probe = bh1770_probe, + .remove = __devexit_p(bh1770_remove), + .id_table = bh1770_id, +}; + +static int __init bh1770_init(void) +{ + return i2c_add_driver(&bh1770_driver); +} + +static void __exit bh1770_exit(void) +{ + i2c_del_driver(&bh1770_driver); +} + +MODULE_DESCRIPTION("BH1770GLC / SFH7770 combined ALS and proximity sensor"); +MODULE_AUTHOR("Samu Onkalo, Nokia Corporation"); +MODULE_LICENSE("GPL v2"); + +module_init(bh1770_init); +module_exit(bh1770_exit); diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c new file mode 100644 index 00000000000..34fe835921c --- /dev/null +++ b/drivers/misc/isl29020.c @@ -0,0 +1,248 @@ +/* + * isl29020.c - Intersil ALS Driver + * + * Copyright (C) 2008 Intel Corp + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data sheet at: http://www.intersil.com/data/fn/fn6505.pdf + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/pm_runtime.h> + +static DEFINE_MUTEX(mutex); + +static ssize_t als_sensing_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int val; + + val = i2c_smbus_read_byte_data(client, 0x00); + + if (val < 0) + return val; + return sprintf(buf, "%d000\n", 1 << (2 * (val & 3))); + +} + +static ssize_t als_lux_input_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret_val, val; + unsigned long int lux; + int temp; + + pm_runtime_get_sync(dev); + msleep(100); + + mutex_lock(&mutex); + temp = i2c_smbus_read_byte_data(client, 0x02); /* MSB data */ + if (temp < 0) { + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + return temp; + } + + ret_val = i2c_smbus_read_byte_data(client, 0x01); /* LSB data */ + mutex_unlock(&mutex); + + if (ret_val < 0) { + pm_runtime_put_sync(dev); + return ret_val; + } + + ret_val |= temp << 8; + val = i2c_smbus_read_byte_data(client, 0x00); + pm_runtime_put_sync(dev); + if (val < 0) + return val; + lux = ((((1 << (2 * (val & 3))))*1000) * ret_val) / 65536; + return sprintf(buf, "%ld\n", lux); +} + +static ssize_t als_sensing_range_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned int ret_val; + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + if (val < 1 || val > 64000) + return -EINVAL; + + /* Pick the smallest sensor range that will meet our requirements */ + if (val <= 1000) + val = 1; + else if (val <= 4000) + val = 2; + else if (val <= 16000) + val = 3; + else + val = 4; + + ret_val = i2c_smbus_read_byte_data(client, 0x00); + + ret_val &= 0xFC; /*reset the bit before setting them */ + ret_val |= val - 1; + ret_val = i2c_smbus_write_byte_data(client, 0x00, ret_val); + + if (ret_val < 0) + return ret_val; + return count; +} + +static void als_set_power_state(struct i2c_client *client, int enable) +{ + int ret_val; + + ret_val = i2c_smbus_read_byte_data(client, 0x00); + if (ret_val < 0) + return; + + if (enable) + ret_val |= 0x80; + else + ret_val &= 0x7F; + + i2c_smbus_write_byte_data(client, 0x00, ret_val); +} + +static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR, + als_sensing_range_show, als_sensing_range_store); +static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux_input_data_show, NULL); + +static struct attribute *mid_att_als[] = { + &dev_attr_lux0_sensor_range.attr, + &dev_attr_lux0_input.attr, + NULL +}; + +static struct attribute_group m_als_gr = { + .name = "isl29020", + .attrs = mid_att_als +}; + +static int als_set_default_config(struct i2c_client *client) +{ + int retval; + + retval = i2c_smbus_write_byte_data(client, 0x00, 0xc0); + if (retval < 0) { + dev_err(&client->dev, "default write failed."); + return retval; + } + return 0;; +} + +static int isl29020_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int res; + + res = als_set_default_config(client); + if (res < 0) + return res; + + res = sysfs_create_group(&client->dev.kobj, &m_als_gr); + if (res) { + dev_err(&client->dev, "isl29020: device create file failed\n"); + return res; + } + dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name); + als_set_power_state(client, 0); + pm_runtime_enable(&client->dev); + return res; +} + +static int isl29020_remove(struct i2c_client *client) +{ + struct als_data *data = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &m_als_gr); + kfree(data); + return 0; +} + +static struct i2c_device_id isl29020_id[] = { + { "isl29020", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, isl29020_id); + +#ifdef CONFIG_PM + +static int isl29020_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + als_set_power_state(client, 0); + return 0; +} + +static int isl29020_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + als_set_power_state(client, 1); + return 0; +} + +static const struct dev_pm_ops isl29020_pm_ops = { + .runtime_suspend = isl29020_runtime_suspend, + .runtime_resume = isl29020_runtime_resume, +}; + +#define ISL29020_PM_OPS (&isl29020_pm_ops) +#else /* CONFIG_PM */ +#define ISL29020_PM_OPS NULL +#endif /* CONFIG_PM */ + +static struct i2c_driver isl29020_driver = { + .driver = { + .name = "isl29020", + .pm = ISL29020_PM_OPS, + }, + .probe = isl29020_probe, + .remove = isl29020_remove, + .id_table = isl29020_id, +}; + +static int __init sensor_isl29020_init(void) +{ + return i2c_add_driver(&isl29020_driver); +} + +static void __exit sensor_isl29020_exit(void) +{ + i2c_del_driver(&isl29020_driver); +} + +module_init(sensor_isl29020_init); +module_exit(sensor_isl29020_exit); + +MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com"); +MODULE_DESCRIPTION("Intersil isl29020 ALS Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 343b5d8ea69..81d7fa4ec0d 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -52,32 +52,32 @@ #define REC_NUM_DEFAULT 10 enum cname { - INVALID, - INT_HARDWARE_ENTRY, - INT_HW_IRQ_EN, - INT_TASKLET_ENTRY, - FS_DEVRW, - MEM_SWAPOUT, - TIMERADD, - SCSI_DISPATCH_CMD, - IDE_CORE_CP, - DIRECT, + CN_INVALID, + CN_INT_HARDWARE_ENTRY, + CN_INT_HW_IRQ_EN, + CN_INT_TASKLET_ENTRY, + CN_FS_DEVRW, + CN_MEM_SWAPOUT, + CN_TIMERADD, + CN_SCSI_DISPATCH_CMD, + CN_IDE_CORE_CP, + CN_DIRECT, }; enum ctype { - NONE, - PANIC, - BUG, - EXCEPTION, - LOOP, - OVERFLOW, - CORRUPT_STACK, - UNALIGNED_LOAD_STORE_WRITE, - OVERWRITE_ALLOCATION, - WRITE_AFTER_FREE, - SOFTLOCKUP, - HARDLOCKUP, - HUNG_TASK, + CT_NONE, + CT_PANIC, + CT_BUG, + CT_EXCEPTION, + CT_LOOP, + CT_OVERFLOW, + CT_CORRUPT_STACK, + CT_UNALIGNED_LOAD_STORE_WRITE, + CT_OVERWRITE_ALLOCATION, + CT_WRITE_AFTER_FREE, + CT_SOFTLOCKUP, + CT_HARDLOCKUP, + CT_HUNG_TASK, }; static char* cp_name[] = { @@ -117,8 +117,8 @@ static char* cpoint_type; static int cpoint_count = DEFAULT_COUNT; static int recur_count = REC_NUM_DEFAULT; -static enum cname cpoint = INVALID; -static enum ctype cptype = NONE; +static enum cname cpoint = CN_INVALID; +static enum ctype cptype = CT_NONE; static int count = DEFAULT_COUNT; module_param(recur_count, int, 0644); @@ -207,12 +207,12 @@ static enum ctype parse_cp_type(const char *what, size_t count) return i + 1; } - return NONE; + return CT_NONE; } static const char *cp_type_to_str(enum ctype type) { - if (type == NONE || type < 0 || type > ARRAY_SIZE(cp_type)) + if (type == CT_NONE || type < 0 || type > ARRAY_SIZE(cp_type)) return "None"; return cp_type[type - 1]; @@ -220,7 +220,7 @@ static const char *cp_type_to_str(enum ctype type) static const char *cp_name_to_str(enum cname name) { - if (name == INVALID || name < 0 || name > ARRAY_SIZE(cp_name)) + if (name == CN_INVALID || name < 0 || name > ARRAY_SIZE(cp_name)) return "INVALID"; return cp_name[name - 1]; @@ -245,7 +245,7 @@ static int lkdtm_parse_commandline(void) return -EINVAL; cptype = parse_cp_type(cpoint_type, strlen(cpoint_type)); - if (cptype == NONE) + if (cptype == CT_NONE) return -EINVAL; for (i = 0; i < ARRAY_SIZE(cp_name); i++) { @@ -274,30 +274,30 @@ static int recursive_loop(int a) static void lkdtm_do_action(enum ctype which) { switch (which) { - case PANIC: + case CT_PANIC: panic("dumptest"); break; - case BUG: + case CT_BUG: BUG(); break; - case EXCEPTION: + case CT_EXCEPTION: *((int *) 0) = 0; break; - case LOOP: + case CT_LOOP: for (;;) ; break; - case OVERFLOW: + case CT_OVERFLOW: (void) recursive_loop(0); break; - case CORRUPT_STACK: { + case CT_CORRUPT_STACK: { volatile u32 data[8]; volatile u32 *p = data; p[12] = 0x12345678; break; } - case UNALIGNED_LOAD_STORE_WRITE: { + case CT_UNALIGNED_LOAD_STORE_WRITE: { static u8 data[5] __attribute__((aligned(4))) = {1, 2, 3, 4, 5}; u32 *p; @@ -309,7 +309,7 @@ static void lkdtm_do_action(enum ctype which) *p = val; break; } - case OVERWRITE_ALLOCATION: { + case CT_OVERWRITE_ALLOCATION: { size_t len = 1020; u32 *data = kmalloc(len, GFP_KERNEL); @@ -317,7 +317,7 @@ static void lkdtm_do_action(enum ctype which) kfree(data); break; } - case WRITE_AFTER_FREE: { + case CT_WRITE_AFTER_FREE: { size_t len = 1024; u32 *data = kmalloc(len, GFP_KERNEL); @@ -326,21 +326,21 @@ static void lkdtm_do_action(enum ctype which) memset(data, 0x78, len); break; } - case SOFTLOCKUP: + case CT_SOFTLOCKUP: preempt_disable(); for (;;) cpu_relax(); break; - case HARDLOCKUP: + case CT_HARDLOCKUP: local_irq_disable(); for (;;) cpu_relax(); break; - case HUNG_TASK: + case CT_HUNG_TASK: set_current_state(TASK_UNINTERRUPTIBLE); schedule(); break; - case NONE: + case CT_NONE: default: break; } @@ -363,43 +363,43 @@ static int lkdtm_register_cpoint(enum cname which) { int ret; - cpoint = INVALID; + cpoint = CN_INVALID; if (lkdtm.entry != NULL) unregister_jprobe(&lkdtm); switch (which) { - case DIRECT: + case CN_DIRECT: lkdtm_do_action(cptype); return 0; - case INT_HARDWARE_ENTRY: + case CN_INT_HARDWARE_ENTRY: lkdtm.kp.symbol_name = "do_IRQ"; lkdtm.entry = (kprobe_opcode_t*) jp_do_irq; break; - case INT_HW_IRQ_EN: + case CN_INT_HW_IRQ_EN: lkdtm.kp.symbol_name = "handle_IRQ_event"; lkdtm.entry = (kprobe_opcode_t*) jp_handle_irq_event; break; - case INT_TASKLET_ENTRY: + case CN_INT_TASKLET_ENTRY: lkdtm.kp.symbol_name = "tasklet_action"; lkdtm.entry = (kprobe_opcode_t*) jp_tasklet_action; break; - case FS_DEVRW: + case CN_FS_DEVRW: lkdtm.kp.symbol_name = "ll_rw_block"; lkdtm.entry = (kprobe_opcode_t*) jp_ll_rw_block; break; - case MEM_SWAPOUT: + case CN_MEM_SWAPOUT: lkdtm.kp.symbol_name = "shrink_inactive_list"; lkdtm.entry = (kprobe_opcode_t*) jp_shrink_inactive_list; break; - case TIMERADD: + case CN_TIMERADD: lkdtm.kp.symbol_name = "hrtimer_start"; lkdtm.entry = (kprobe_opcode_t*) jp_hrtimer_start; break; - case SCSI_DISPATCH_CMD: + case CN_SCSI_DISPATCH_CMD: lkdtm.kp.symbol_name = "scsi_dispatch_cmd"; lkdtm.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd; break; - case IDE_CORE_CP: + case CN_IDE_CORE_CP: #ifdef CONFIG_IDE lkdtm.kp.symbol_name = "generic_ide_ioctl"; lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl; @@ -416,7 +416,7 @@ static int lkdtm_register_cpoint(enum cname which) cpoint = which; if ((ret = register_jprobe(&lkdtm)) < 0) { printk(KERN_INFO "lkdtm: Couldn't register jprobe\n"); - cpoint = INVALID; + cpoint = CN_INVALID; } return ret; @@ -445,7 +445,7 @@ static ssize_t do_register_entry(enum cname which, struct file *f, cptype = parse_cp_type(buf, count); free_page((unsigned long) buf); - if (cptype == NONE) + if (cptype == CT_NONE) return -EINVAL; err = lkdtm_register_cpoint(which); @@ -487,49 +487,49 @@ static int lkdtm_debugfs_open(struct inode *inode, struct file *file) static ssize_t int_hardware_entry(struct file *f, const char __user *buf, size_t count, loff_t *off) { - return do_register_entry(INT_HARDWARE_ENTRY, f, buf, count, off); + return do_register_entry(CN_INT_HARDWARE_ENTRY, f, buf, count, off); } static ssize_t int_hw_irq_en(struct file *f, const char __user *buf, size_t count, loff_t *off) { - return do_register_entry(INT_HW_IRQ_EN, f, buf, count, off); + return do_register_entry(CN_INT_HW_IRQ_EN, f, buf, count, off); } static ssize_t int_tasklet_entry(struct file *f, const char __user *buf, size_t count, loff_t *off) { - return do_register_entry(INT_TASKLET_ENTRY, f, buf, count, off); + return do_register_entry(CN_INT_TASKLET_ENTRY, f, buf, count, off); } static ssize_t fs_devrw_entry(struct file *f, const char __user *buf, size_t count, loff_t *off) { - return do_register_entry(FS_DEVRW, f, buf, count, off); + return do_register_entry(CN_FS_DEVRW, f, buf, count, off); } static ssize_t mem_swapout_entry(struct file *f, const char __user *buf, size_t count, loff_t *off) { - return do_register_entry(MEM_SWAPOUT, f, buf, count, off); + return do_register_entry(CN_MEM_SWAPOUT, f, buf, count, off); } static ssize_t timeradd_entry(struct file *f, const char __user *buf, size_t count, loff_t *off) { - return do_register_entry(TIMERADD, f, buf, count, off); + return do_register_entry(CN_TIMERADD, f, buf, count, off); } static ssize_t scsi_dispatch_cmd_entry(struct file *f, const char __user *buf, size_t count, loff_t *off) { - return do_register_entry(SCSI_DISPATCH_CMD, f, buf, count, off); + return do_register_entry(CN_SCSI_DISPATCH_CMD, f, buf, count, off); } static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf, size_t count, loff_t *off) { - return do_register_entry(IDE_CORE_CP, f, buf, count, off); + return do_register_entry(CN_IDE_CORE_CP, f, buf, count, off); } /* Special entry to just crash directly. Available without KPROBEs */ @@ -557,7 +557,7 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf, type = parse_cp_type(buf, count); free_page((unsigned long) buf); - if (type == NONE) + if (type == CT_NONE) return -EINVAL; printk(KERN_INFO "lkdtm: Performing direct entry %s\n", @@ -649,7 +649,7 @@ static int __init lkdtm_module_init(void) goto out_err; } - if (cpoint != INVALID && cptype != NONE) { + if (cpoint != CN_INVALID && cptype != CT_NONE) { ret = lkdtm_register_cpoint(cpoint); if (ret < 0) { printk(KERN_INFO "lkdtm: Invalid crash point %d\n", diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 4197a3cb26b..b05db55c8c8 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -343,8 +343,10 @@ static int __devinit phantom_probe(struct pci_dev *pdev, int retval; retval = pci_enable_device(pdev); - if (retval) + if (retval) { + dev_err(&pdev->dev, "pci_enable_device failed!\n"); goto err; + } minor = phantom_get_free(); if (minor == PHANTOM_MAX_MINORS) { @@ -356,8 +358,10 @@ static int __devinit phantom_probe(struct pci_dev *pdev, phantom_devices[minor] = 1; retval = pci_request_regions(pdev, "phantom"); - if (retval) + if (retval) { + dev_err(&pdev->dev, "pci_request_regions failed!\n"); goto err_null; + } retval = -ENOMEM; pht = kzalloc(sizeof(*pht), GFP_KERNEL); diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c index 1f59ee2226c..17bbacb1b4b 100644 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ b/drivers/misc/sgi-xp/xpc_uv.c @@ -417,6 +417,7 @@ xpc_process_activate_IRQ_rcvd_uv(void) static void xpc_handle_activate_mq_msg_uv(struct xpc_partition *part, struct xpc_activate_mq_msghdr_uv *msg_hdr, + int part_setup, int *wakeup_hb_checker) { unsigned long irq_flags; @@ -481,6 +482,9 @@ xpc_handle_activate_mq_msg_uv(struct xpc_partition *part, case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV: { struct xpc_activate_mq_msg_chctl_closerequest_uv *msg; + if (!part_setup) + break; + msg = container_of(msg_hdr, struct xpc_activate_mq_msg_chctl_closerequest_uv, hdr); @@ -497,6 +501,9 @@ xpc_handle_activate_mq_msg_uv(struct xpc_partition *part, case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV: { struct xpc_activate_mq_msg_chctl_closereply_uv *msg; + if (!part_setup) + break; + msg = container_of(msg_hdr, struct xpc_activate_mq_msg_chctl_closereply_uv, hdr); @@ -511,6 +518,9 @@ xpc_handle_activate_mq_msg_uv(struct xpc_partition *part, case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV: { struct xpc_activate_mq_msg_chctl_openrequest_uv *msg; + if (!part_setup) + break; + msg = container_of(msg_hdr, struct xpc_activate_mq_msg_chctl_openrequest_uv, hdr); @@ -528,6 +538,9 @@ xpc_handle_activate_mq_msg_uv(struct xpc_partition *part, case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV: { struct xpc_activate_mq_msg_chctl_openreply_uv *msg; + if (!part_setup) + break; + msg = container_of(msg_hdr, struct xpc_activate_mq_msg_chctl_openreply_uv, hdr); args = &part->remote_openclose_args[msg->ch_number]; @@ -545,6 +558,9 @@ xpc_handle_activate_mq_msg_uv(struct xpc_partition *part, case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV: { struct xpc_activate_mq_msg_chctl_opencomplete_uv *msg; + if (!part_setup) + break; + msg = container_of(msg_hdr, struct xpc_activate_mq_msg_chctl_opencomplete_uv, hdr); spin_lock_irqsave(&part->chctl_lock, irq_flags); @@ -621,6 +637,7 @@ xpc_handle_activate_IRQ_uv(int irq, void *dev_id) part_referenced = xpc_part_ref(part); xpc_handle_activate_mq_msg_uv(part, msg_hdr, + part_referenced, &wakeup_hb_checker); if (part_referenced) xpc_part_deref(part); diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 68d12794cfd..1a0261160e5 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -237,7 +237,7 @@ endchoice config MMC_ATMELMCI_DMA bool "Atmel MCI DMA support (EXPERIMENTAL)" - depends on MMC_ATMELMCI && AVR32 && DMA_ENGINE && EXPERIMENTAL + depends on MMC_ATMELMCI && (AVR32 || ARCH_AT91SAM9G45) && DMA_ENGINE && EXPERIMENTAL help Say Y here to have the Atmel MCI driver use a DMA engine to do data transfers and thus increase the throughput and diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4526d2791f2..4693e62145a 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -364,6 +364,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) { struct regulator *reg; int ret = 0; + int ocr_value = 0; switch (host->id) { case OMAP_MMC1_DEVID: @@ -396,6 +397,17 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) } } else { host->vcc = reg; + ocr_value = mmc_regulator_get_ocrmask(reg); + if (!mmc_slot(host).ocr_mask) { + mmc_slot(host).ocr_mask = ocr_value; + } else { + if (!(mmc_slot(host).ocr_mask & ocr_value)) { + pr_err("MMC%d ocrmask %x is not supported\n", + host->id, mmc_slot(host).ocr_mask); + mmc_slot(host).ocr_mask = 0; + return -EINVAL; + } + } mmc_slot(host).ocr_mask = mmc_regulator_get_ocrmask(reg); /* Allow an aux regulator */ @@ -982,6 +994,17 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | bit); + /* + * OMAP4 ES2 and greater has an updated reset logic. + * Monitor a 0->1 transition first + */ + if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) { + while ((!(OMAP_HSMMC_READ(host, SYSCTL) & bit)) + && (i++ < limit)) + cpu_relax(); + } + i = 0; + while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) && (i++ < limit)) cpu_relax(); @@ -2003,6 +2026,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) if (res == NULL || irq < 0) return -ENXIO; + res->start += pdata->reg_offset; + res->end += pdata->reg_offset; res = request_mem_region(res->start, res->end - res->start + 1, pdev->name); if (res == NULL) @@ -2116,23 +2141,9 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE; - switch (mmc_slot(host).wires) { - case 8: - mmc->caps |= MMC_CAP_8_BIT_DATA; - /* Fall through */ - case 4: + mmc->caps |= mmc_slot(host).caps; + if (mmc->caps & MMC_CAP_8_BIT_DATA) mmc->caps |= MMC_CAP_4_BIT_DATA; - break; - case 1: - /* Nothing to crib here */ - case 0: - /* Assuming nothing was given by board, Core use's 1-Bit */ - break; - default: - /* Completely unexpected.. Core goes with 1-Bit Width */ - dev_crit(mmc_dev(host->mmc), "Invalid width %d\n used!" - "using 1 instead\n", mmc_slot(host).wires); - } if (mmc_slot(host).nonremovable) mmc->caps |= MMC_CAP_NONREMOVABLE; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 702f598f495..86fe67fd49b 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -883,14 +883,6 @@ config BFIN_RX_DESC_NUM help Set the number of buffer packets used in driver. -config BFIN_MAC_RMII - bool "RMII PHY Interface" - depends on BFIN_MAC - default y if BFIN527_EZKIT - default n if BFIN537_STAMP - help - Use Reduced PHY MII Interface - config BFIN_MAC_USE_HWSTAMP bool "Use IEEE 1588 hwstamp" depends on BFIN_MAC && BF518 diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index f7233191162..ce1e5e9d06f 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -1,7 +1,7 @@ /* * Blackfin On-Chip MAC Driver * - * Copyright 2004-2007 Analog Devices Inc. + * Copyright 2004-2010 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -23,7 +23,6 @@ #include <linux/device.h> #include <linux/spinlock.h> #include <linux/mii.h> -#include <linux/phy.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> @@ -76,12 +75,6 @@ static struct net_dma_desc_tx *current_tx_ptr; static struct net_dma_desc_tx *tx_desc; static struct net_dma_desc_rx *rx_desc; -#if defined(CONFIG_BFIN_MAC_RMII) -static u16 pin_req[] = P_RMII0; -#else -static u16 pin_req[] = P_MII0; -#endif - static void desc_list_free(void) { struct net_dma_desc_rx *r; @@ -347,23 +340,23 @@ static void bfin_mac_adjust_link(struct net_device *dev) } if (phydev->speed != lp->old_speed) { -#if defined(CONFIG_BFIN_MAC_RMII) - u32 opmode = bfin_read_EMAC_OPMODE(); - switch (phydev->speed) { - case 10: - opmode |= RMII_10; - break; - case 100: - opmode &= ~(RMII_10); - break; - default: - printk(KERN_WARNING - "%s: Ack! Speed (%d) is not 10/100!\n", - DRV_NAME, phydev->speed); - break; + if (phydev->interface == PHY_INTERFACE_MODE_RMII) { + u32 opmode = bfin_read_EMAC_OPMODE(); + switch (phydev->speed) { + case 10: + opmode |= RMII_10; + break; + case 100: + opmode &= ~RMII_10; + break; + default: + printk(KERN_WARNING + "%s: Ack! Speed (%d) is not 10/100!\n", + DRV_NAME, phydev->speed); + break; + } + bfin_write_EMAC_OPMODE(opmode); } - bfin_write_EMAC_OPMODE(opmode); -#endif new_state = 1; lp->old_speed = phydev->speed; @@ -392,7 +385,7 @@ static void bfin_mac_adjust_link(struct net_device *dev) /* MDC = 2.5 MHz */ #define MDC_CLK 2500000 -static int mii_probe(struct net_device *dev) +static int mii_probe(struct net_device *dev, int phy_mode) { struct bfin_mac_local *lp = netdev_priv(dev); struct phy_device *phydev = NULL; @@ -411,8 +404,8 @@ static int mii_probe(struct net_device *dev) sysctl = (sysctl & ~MDCDIV) | SET_MDCDIV(mdc_div); bfin_write_EMAC_SYSCTL(sysctl); - /* search for connect PHY device */ - for (i = 0; i < PHY_MAX_ADDR; i++) { + /* search for connected PHY device */ + for (i = 0; i < PHY_MAX_ADDR; ++i) { struct phy_device *const tmp_phydev = lp->mii_bus->phy_map[i]; if (!tmp_phydev) @@ -429,13 +422,14 @@ static int mii_probe(struct net_device *dev) return -ENODEV; } -#if defined(CONFIG_BFIN_MAC_RMII) - phydev = phy_connect(dev, dev_name(&phydev->dev), &bfin_mac_adjust_link, - 0, PHY_INTERFACE_MODE_RMII); -#else + if (phy_mode != PHY_INTERFACE_MODE_RMII && + phy_mode != PHY_INTERFACE_MODE_MII) { + printk(KERN_INFO "%s: Invalid phy interface mode\n", dev->name); + return -EINVAL; + } + phydev = phy_connect(dev, dev_name(&phydev->dev), &bfin_mac_adjust_link, - 0, PHY_INTERFACE_MODE_MII); -#endif + 0, phy_mode); if (IS_ERR(phydev)) { printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); @@ -570,6 +564,8 @@ static const struct ethtool_ops bfin_mac_ethtool_ops = { /**************************************************************************/ void setup_system_regs(struct net_device *dev) { + struct bfin_mac_local *lp = netdev_priv(dev); + int i; unsigned short sysctl; /* @@ -577,6 +573,15 @@ void setup_system_regs(struct net_device *dev) * Configure checksum support and rcve frame word alignment */ sysctl = bfin_read_EMAC_SYSCTL(); + /* + * check if interrupt is requested for any PHY, + * enable PHY interrupt only if needed + */ + for (i = 0; i < PHY_MAX_ADDR; ++i) + if (lp->mii_bus->irq[i] != PHY_POLL) + break; + if (i < PHY_MAX_ADDR) + sysctl |= PHYIE; sysctl |= RXDWA; #if defined(BFIN_MAC_CSUM_OFFLOAD) sysctl |= RXCKS; @@ -1203,7 +1208,7 @@ static void bfin_mac_disable(void) /* * Enable Interrupts, Receive, and Transmit */ -static int bfin_mac_enable(void) +static int bfin_mac_enable(struct phy_device *phydev) { int ret; u32 opmode; @@ -1233,12 +1238,13 @@ static int bfin_mac_enable(void) opmode |= DRO | DC | PSF; opmode |= RE; -#if defined(CONFIG_BFIN_MAC_RMII) - opmode |= RMII; /* For Now only 100MBit are supported */ + if (phydev->interface == PHY_INTERFACE_MODE_RMII) { + opmode |= RMII; /* For Now only 100MBit are supported */ #if (defined(CONFIG_BF537) || defined(CONFIG_BF536)) && CONFIG_BF_REV_0_2 - opmode |= TE; -#endif + opmode |= TE; #endif + } + /* Turn on the EMAC rx */ bfin_write_EMAC_OPMODE(opmode); @@ -1270,7 +1276,7 @@ static void bfin_mac_timeout(struct net_device *dev) if (netif_queue_stopped(lp->ndev)) netif_wake_queue(lp->ndev); - bfin_mac_enable(); + bfin_mac_enable(lp->phydev); /* We can accept TX packets again */ dev->trans_start = jiffies; /* prevent tx timeout */ @@ -1342,11 +1348,19 @@ static void bfin_mac_set_multicast_list(struct net_device *dev) static int bfin_mac_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { + struct bfin_mac_local *lp = netdev_priv(netdev); + + if (!netif_running(netdev)) + return -EINVAL; + switch (cmd) { case SIOCSHWTSTAMP: return bfin_mac_hwtstamp_ioctl(netdev, ifr, cmd); default: - return -EOPNOTSUPP; + if (lp->phydev) + return phy_mii_ioctl(lp->phydev, ifr, cmd); + else + return -EOPNOTSUPP; } } @@ -1394,7 +1408,7 @@ static int bfin_mac_open(struct net_device *dev) setup_mac_addr(dev->dev_addr); bfin_mac_disable(); - ret = bfin_mac_enable(); + ret = bfin_mac_enable(lp->phydev); if (ret) return ret; pr_debug("hardware init finished\n"); @@ -1450,6 +1464,7 @@ static int __devinit bfin_mac_probe(struct platform_device *pdev) struct net_device *ndev; struct bfin_mac_local *lp; struct platform_device *pd; + struct bfin_mii_bus_platform_data *mii_bus_data; int rc; ndev = alloc_etherdev(sizeof(struct bfin_mac_local)); @@ -1501,11 +1516,12 @@ static int __devinit bfin_mac_probe(struct platform_device *pdev) if (!lp->mii_bus) { dev_err(&pdev->dev, "Cannot get mii_bus!\n"); rc = -ENODEV; - goto out_err_mii_bus_probe; + goto out_err_probe_mac; } lp->mii_bus->priv = ndev; + mii_bus_data = pd->dev.platform_data; - rc = mii_probe(ndev); + rc = mii_probe(ndev, mii_bus_data->phy_mode); if (rc) { dev_err(&pdev->dev, "MII Probe failed!\n"); goto out_err_mii_probe; @@ -1552,8 +1568,6 @@ out_err_request_irq: out_err_mii_probe: mdiobus_unregister(lp->mii_bus); mdiobus_free(lp->mii_bus); -out_err_mii_bus_probe: - peripheral_free_list(pin_req); out_err_probe_mac: platform_set_drvdata(pdev, NULL); free_netdev(ndev); @@ -1576,8 +1590,6 @@ static int __devexit bfin_mac_remove(struct platform_device *pdev) free_netdev(ndev); - peripheral_free_list(pin_req); - return 0; } @@ -1623,12 +1635,21 @@ static int bfin_mac_resume(struct platform_device *pdev) static int __devinit bfin_mii_bus_probe(struct platform_device *pdev) { struct mii_bus *miibus; + struct bfin_mii_bus_platform_data *mii_bus_pd; + const unsigned short *pin_req; int rc, i; + mii_bus_pd = dev_get_platdata(&pdev->dev); + if (!mii_bus_pd) { + dev_err(&pdev->dev, "No peripherals in platform data!\n"); + return -EINVAL; + } + /* * We are setting up a network card, * so set the GPIO pins to Ethernet mode */ + pin_req = mii_bus_pd->mac_peripherals; rc = peripheral_request_list(pin_req, DRV_NAME); if (rc) { dev_err(&pdev->dev, "Requesting peripherals failed!\n"); @@ -1645,13 +1666,30 @@ static int __devinit bfin_mii_bus_probe(struct platform_device *pdev) miibus->parent = &pdev->dev; miibus->name = "bfin_mii_bus"; + miibus->phy_mask = mii_bus_pd->phy_mask; + snprintf(miibus->id, MII_BUS_ID_SIZE, "0"); miibus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); - if (miibus->irq == NULL) - goto out_err_alloc; - for (i = 0; i < PHY_MAX_ADDR; ++i) + if (!miibus->irq) + goto out_err_irq_alloc; + + for (i = rc; i < PHY_MAX_ADDR; ++i) miibus->irq[i] = PHY_POLL; + rc = clamp(mii_bus_pd->phydev_number, 0, PHY_MAX_ADDR); + if (rc != mii_bus_pd->phydev_number) + dev_err(&pdev->dev, "Invalid number (%i) of phydevs\n", + mii_bus_pd->phydev_number); + for (i = 0; i < rc; ++i) { + unsigned short phyaddr = mii_bus_pd->phydev_data[i].addr; + if (phyaddr < PHY_MAX_ADDR) + miibus->irq[phyaddr] = mii_bus_pd->phydev_data[i].irq; + else + dev_err(&pdev->dev, + "Invalid PHY address %i for phydev %i\n", + phyaddr, i); + } + rc = mdiobus_register(miibus); if (rc) { dev_err(&pdev->dev, "Cannot register MDIO bus!\n"); @@ -1663,6 +1701,7 @@ static int __devinit bfin_mii_bus_probe(struct platform_device *pdev) out_err_mdiobus_register: kfree(miibus->irq); +out_err_irq_alloc: mdiobus_free(miibus); out_err_alloc: peripheral_free_list(pin_req); @@ -1673,11 +1712,15 @@ out_err_alloc: static int __devexit bfin_mii_bus_remove(struct platform_device *pdev) { struct mii_bus *miibus = platform_get_drvdata(pdev); + struct bfin_mii_bus_platform_data *mii_bus_pd = + dev_get_platdata(&pdev->dev); + platform_set_drvdata(pdev, NULL); mdiobus_unregister(miibus); kfree(miibus->irq); mdiobus_free(miibus); - peripheral_free_list(pin_req); + peripheral_free_list(mii_bus_pd->mac_peripherals); + return 0; } diff --git a/drivers/net/bfin_mac.h b/drivers/net/bfin_mac.h index 04e4050df18..aed68bed236 100644 --- a/drivers/net/bfin_mac.h +++ b/drivers/net/bfin_mac.h @@ -14,6 +14,8 @@ #include <linux/clocksource.h> #include <linux/timecompare.h> #include <linux/timer.h> +#include <linux/etherdevice.h> +#include <linux/bfin_mac.h> #define BFIN_MAC_CSUM_OFFLOAD diff --git a/drivers/net/mlx4/en_main.c b/drivers/net/mlx4/en_main.c index 14390641704..f6e0d40cd87 100644 --- a/drivers/net/mlx4/en_main.c +++ b/drivers/net/mlx4/en_main.c @@ -124,6 +124,13 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev) return 0; } +static void *mlx4_en_get_netdev(struct mlx4_dev *dev, void *ctx, u8 port) +{ + struct mlx4_en_dev *endev = ctx; + + return endev->pndev[port]; +} + static void mlx4_en_event(struct mlx4_dev *dev, void *endev_ptr, enum mlx4_dev_event event, int port) { @@ -282,9 +289,11 @@ err_free_res: } static struct mlx4_interface mlx4_en_interface = { - .add = mlx4_en_add, - .remove = mlx4_en_remove, - .event = mlx4_en_event, + .add = mlx4_en_add, + .remove = mlx4_en_remove, + .event = mlx4_en_event, + .get_dev = mlx4_en_get_netdev, + .protocol = MLX4_PROTOCOL_EN, }; static int __init mlx4_en_init(void) diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c index 79478bd4211..6d6806b361e 100644 --- a/drivers/net/mlx4/en_netdev.c +++ b/drivers/net/mlx4/en_netdev.c @@ -69,6 +69,7 @@ static void mlx4_en_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; int err; + int idx; if (!priv->vlgrp) return; @@ -83,7 +84,10 @@ static void mlx4_en_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) if (err) en_err(priv, "Failed configuring VLAN filter\n"); } + if (mlx4_register_vlan(mdev->dev, priv->port, vid, &idx)) + en_err(priv, "failed adding vlan %d\n", vid); mutex_unlock(&mdev->state_lock); + } static void mlx4_en_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) @@ -91,6 +95,7 @@ static void mlx4_en_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; int err; + int idx; if (!priv->vlgrp) return; @@ -101,6 +106,11 @@ static void mlx4_en_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) /* Remove VID from port VLAN filter */ mutex_lock(&mdev->state_lock); + if (!mlx4_find_cached_vlan(mdev->dev, priv->port, vid, &idx)) + mlx4_unregister_vlan(mdev->dev, priv->port, idx); + else + en_err(priv, "could not find vid %d in cache\n", vid); + if (mdev->device_up && priv->port_up) { err = mlx4_SET_VLAN_FLTR(mdev->dev, priv->port, priv->vlgrp); if (err) diff --git a/drivers/net/mlx4/en_port.c b/drivers/net/mlx4/en_port.c index aa3ef2aee5b..7f5a3221e0c 100644 --- a/drivers/net/mlx4/en_port.c +++ b/drivers/net/mlx4/en_port.c @@ -127,8 +127,8 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, memset(context, 0, sizeof *context); context->base_qpn = cpu_to_be32(base_qpn); - context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_SHIFT | base_qpn); - context->mcast = cpu_to_be32(1 << SET_PORT_PROMISC_SHIFT | base_qpn); + context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_EN_SHIFT | base_qpn); + context->mcast = cpu_to_be32(1 << SET_PORT_PROMISC_MODE_SHIFT | base_qpn); context->intra_no_vlan = 0; context->no_vlan = MLX4_NO_VLAN_IDX; context->intra_vlan_miss = 0; diff --git a/drivers/net/mlx4/en_port.h b/drivers/net/mlx4/en_port.h index f6511aa2b7d..092e814b198 100644 --- a/drivers/net/mlx4/en_port.h +++ b/drivers/net/mlx4/en_port.h @@ -36,7 +36,8 @@ #define SET_PORT_GEN_ALL_VALID 0x7 -#define SET_PORT_PROMISC_SHIFT 31 +#define SET_PORT_PROMISC_EN_SHIFT 31 +#define SET_PORT_PROMISC_MODE_SHIFT 30 enum { MLX4_CMD_SET_VLAN_FLTR = 0x47, diff --git a/drivers/net/mlx4/fw.c b/drivers/net/mlx4/fw.c index b716e1a1b29..b68eee2414c 100644 --- a/drivers/net/mlx4/fw.c +++ b/drivers/net/mlx4/fw.c @@ -98,7 +98,8 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u32 flags) [20] = "Address vector port checking support", [21] = "UD multicast support", [24] = "Demand paging support", - [25] = "Router support" + [25] = "Router support", + [30] = "IBoE support" }; int i; diff --git a/drivers/net/mlx4/intf.c b/drivers/net/mlx4/intf.c index 55506780275..73c94fcdfdd 100644 --- a/drivers/net/mlx4/intf.c +++ b/drivers/net/mlx4/intf.c @@ -161,3 +161,24 @@ void mlx4_unregister_device(struct mlx4_dev *dev) mutex_unlock(&intf_mutex); } + +void *mlx4_get_protocol_dev(struct mlx4_dev *dev, enum mlx4_protocol proto, int port) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_device_context *dev_ctx; + unsigned long flags; + void *result = NULL; + + spin_lock_irqsave(&priv->ctx_lock, flags); + + list_for_each_entry(dev_ctx, &priv->ctx_list, list) + if (dev_ctx->intf->protocol == proto && dev_ctx->intf->get_dev) { + result = dev_ctx->intf->get_dev(dev, dev_ctx->context, port); + break; + } + + spin_unlock_irqrestore(&priv->ctx_lock, flags); + + return result; +} +EXPORT_SYMBOL_GPL(mlx4_get_protocol_dev); diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index 569fa3df381..782f11d8fa7 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c @@ -103,7 +103,7 @@ MODULE_PARM_DESC(use_prio, "Enable steering by VLAN priority on ETH ports " static int log_mtts_per_seg = ilog2(MLX4_MTT_ENTRY_PER_SEG); module_param_named(log_mtts_per_seg, log_mtts_per_seg, int, 0444); -MODULE_PARM_DESC(log_mtts_per_seg, "Log2 number of MTT entries per segment (1-5)"); +MODULE_PARM_DESC(log_mtts_per_seg, "Log2 number of MTT entries per segment (1-7)"); int mlx4_check_port_params(struct mlx4_dev *dev, enum mlx4_port_type *port_type) @@ -1310,7 +1310,7 @@ static int __init mlx4_verify_params(void) return -1; } - if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 5)) { + if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 7)) { pr_warning("mlx4_core: bad log_mtts_per_seg: %d\n", log_mtts_per_seg); return -1; } diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h index 1fc16ab7ad2..dfed6a07c2d 100644 --- a/drivers/net/mlx4/mlx4_en.h +++ b/drivers/net/mlx4/mlx4_en.h @@ -475,6 +475,7 @@ struct mlx4_en_priv { char *mc_addrs; int mc_addrs_cnt; struct mlx4_en_stat_out_mbox hw_stats; + int vids[128]; }; diff --git a/drivers/net/mlx4/port.c b/drivers/net/mlx4/port.c index 606aa58afde..56371ef328e 100644 --- a/drivers/net/mlx4/port.c +++ b/drivers/net/mlx4/port.c @@ -182,6 +182,25 @@ static int mlx4_set_port_vlan_table(struct mlx4_dev *dev, u8 port, return err; } +int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx) +{ + struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table; + int i; + + for (i = 0; i < MLX4_MAX_VLAN_NUM; ++i) { + if (table->refs[i] && + (vid == (MLX4_VLAN_MASK & + be32_to_cpu(table->entries[i])))) { + /* VLAN already registered, increase reference count */ + *idx = i; + return 0; + } + } + + return -ENOENT; +} +EXPORT_SYMBOL_GPL(mlx4_find_cached_vlan); + int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index) { struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table; diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c index f3f8be5a35f..14f0955eca6 100644 --- a/drivers/net/xilinx_emaclite.c +++ b/drivers/net/xilinx_emaclite.c @@ -430,8 +430,8 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) } /* Get the protocol type of the ethernet frame that arrived */ - proto_type = ((in_be32(addr + XEL_HEADER_OFFSET + - XEL_RXBUFF_OFFSET) >> XEL_HEADER_SHIFT) & + proto_type = ((ntohl(in_be32(addr + XEL_HEADER_OFFSET + + XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & XEL_RPLR_LENGTH_MASK); /* Check if received ethernet frame is a raw ethernet frame @@ -439,9 +439,9 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) { if (proto_type == ETH_P_IP) { - length = ((in_be32(addr + + length = ((ntohl(in_be32(addr + XEL_HEADER_IP_LENGTH_OFFSET + - XEL_RXBUFF_OFFSET) >> + XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & XEL_RPLR_LENGTH_MASK); length += ETH_HLEN + ETH_FCS_LEN; diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index 0bab84ebb15..19bc7369547 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -12,11 +12,12 @@ void pnp_unregister_protocol(struct pnp_protocol *protocol); #define PNP_EISA_ID_MASK 0x7fffffff void pnp_eisa_id_to_string(u32 id, char *str); -struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *, int id, char *pnpid); +struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *, int id, + const char *pnpid); struct pnp_card *pnp_alloc_card(struct pnp_protocol *, int id, char *pnpid); int pnp_add_device(struct pnp_dev *dev); -struct pnp_id *pnp_add_id(struct pnp_dev *dev, char *id); +struct pnp_id *pnp_add_id(struct pnp_dev *dev, const char *id); int pnp_add_card(struct pnp_card *card); void pnp_remove_card(struct pnp_card *card); diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index 88b3cde5259..0f34d962fd3 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -124,7 +124,8 @@ static void pnp_release_device(struct device *dmdev) kfree(dev); } -struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id, char *pnpid) +struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id, + const char *pnpid) { struct pnp_dev *dev; struct pnp_id *dev_id; @@ -194,8 +195,9 @@ int pnp_add_device(struct pnp_dev *dev) for (id = dev->id; id; id = id->next) len += scnprintf(buf + len, sizeof(buf) - len, " %s", id->id); - pnp_dbg(&dev->dev, "%s device, IDs%s (%s)\n", - dev->protocol->name, buf, dev->active ? "active" : "disabled"); + dev_printk(KERN_DEBUG, &dev->dev, "%s device, IDs%s (%s)\n", + dev->protocol->name, buf, + dev->active ? "active" : "disabled"); return 0; } diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index cd11b113494..d1dbb9df53f 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -236,7 +236,7 @@ void pnp_unregister_driver(struct pnp_driver *drv) * @dev: pointer to the desired device * @id: pointer to an EISA id string */ -struct pnp_id *pnp_add_id(struct pnp_dev *dev, char *id) +struct pnp_id *pnp_add_id(struct pnp_dev *dev, const char *id) { struct pnp_id *dev_id, *ptr; diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index dc4e32e031e..2d73dfcecdb 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -28,7 +28,7 @@ #include "../base.h" #include "pnpacpi.h" -static int num = 0; +static int num; /* We need only to blacklist devices that have already an acpi driver that * can't use pnp layer. We don't need to blacklist device that are directly @@ -59,7 +59,7 @@ static inline int __init is_exclusive_device(struct acpi_device *dev) #define TEST_ALPHA(c) \ if (!('@' <= (c) || (c) <= 'Z')) \ return 0 -static int __init ispnpidacpi(char *id) +static int __init ispnpidacpi(const char *id) { TEST_ALPHA(id[0]); TEST_ALPHA(id[1]); @@ -180,11 +180,24 @@ struct pnp_protocol pnpacpi_protocol = { }; EXPORT_SYMBOL(pnpacpi_protocol); +static char *pnpacpi_get_id(struct acpi_device *device) +{ + struct acpi_hardware_id *id; + + list_for_each_entry(id, &device->pnp.ids, list) { + if (ispnpidacpi(id->id)) + return id->id; + } + + return NULL; +} + static int __init pnpacpi_add_device(struct acpi_device *device) { acpi_handle temp = NULL; acpi_status status; struct pnp_dev *dev; + char *pnpid; struct acpi_hardware_id *id; /* @@ -192,11 +205,17 @@ static int __init pnpacpi_add_device(struct acpi_device *device) * driver should not be loaded. */ status = acpi_get_handle(device->handle, "_CRS", &temp); - if (ACPI_FAILURE(status) || !ispnpidacpi(acpi_device_hid(device)) || - is_exclusive_device(device) || (!device->status.present)) + if (ACPI_FAILURE(status)) + return 0; + + pnpid = pnpacpi_get_id(device); + if (!pnpid) + return 0; + + if (is_exclusive_device(device) || !device->status.present) return 0; - dev = pnp_alloc_dev(&pnpacpi_protocol, num, acpi_device_hid(device)); + dev = pnp_alloc_dev(&pnpacpi_protocol, num, pnpid); if (!dev) return -ENOMEM; @@ -227,7 +246,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device) pnpacpi_parse_resource_option_data(dev); list_for_each_entry(id, &device->pnp.ids, list) { - if (!strcmp(id->id, acpi_device_hid(device))) + if (!strcmp(id->id, pnpid)) continue; if (!ispnpidacpi(id->id)) continue; diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index e3446ab8b56..a925e6b63d7 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -523,7 +523,7 @@ struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq, res->start = irq; res->end = irq; - pnp_dbg(&dev->dev, " add %pr\n", res); + dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); return pnp_res; } @@ -544,7 +544,7 @@ struct pnp_resource *pnp_add_dma_resource(struct pnp_dev *dev, int dma, res->start = dma; res->end = dma; - pnp_dbg(&dev->dev, " add %pr\n", res); + dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); return pnp_res; } @@ -568,7 +568,7 @@ struct pnp_resource *pnp_add_io_resource(struct pnp_dev *dev, res->start = start; res->end = end; - pnp_dbg(&dev->dev, " add %pr\n", res); + dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); return pnp_res; } @@ -592,7 +592,7 @@ struct pnp_resource *pnp_add_mem_resource(struct pnp_dev *dev, res->start = start; res->end = end; - pnp_dbg(&dev->dev, " add %pr\n", res); + dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); return pnp_res; } @@ -616,7 +616,7 @@ struct pnp_resource *pnp_add_bus_resource(struct pnp_dev *dev, res->start = start; res->end = end; - pnp_dbg(&dev->dev, " add %pr\n", res); + dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); return pnp_res; } diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 07343568a12..60d83d983a3 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -64,8 +64,7 @@ config TEST_POWER config BATTERY_DS2760 tristate "DS2760 battery driver (HP iPAQ & others)" - select W1 - select W1_SLAVE_DS2760 + depends on W1 && W1_SLAVE_DS2760 help Say Y here to enable support for batteries with ds2760 chip. @@ -109,6 +108,13 @@ config BATTERY_WM97XX help Say Y to enable support for battery measured by WM97xx aux port. +config BATTERY_BQ20Z75 + tristate "TI BQ20z75 gas gauge" + depends on I2C + help + Say Y to include support for TI BQ20z75 SBS-compliant + gas gauge and protection IC. + config BATTERY_BQ27x00 tristate "BQ27x00 battery driver" depends on I2C @@ -166,4 +172,17 @@ config BATTERY_INTEL_MID Say Y here to enable the battery driver on Intel MID platforms. +config CHARGER_ISP1704 + tristate "ISP1704 USB Charger Detection" + depends on USB_OTG_UTILS + help + Say Y to enable support for USB Charger Detection with + ISP1707/ISP1704 USB transceivers. + +config CHARGER_TWL4030 + tristate "OMAP TWL4030 BCI charger driver" + depends on TWL4030_CORE + help + Say Y here to enable support for TWL4030 Battery Charge Interface. + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 10143aaf4ee..c75772eb157 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -1,16 +1,8 @@ -power_supply-objs := power_supply_core.o +ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG -ifeq ($(CONFIG_SYSFS),y) -power_supply-objs += power_supply_sysfs.o -endif - -ifeq ($(CONFIG_LEDS_TRIGGERS),y) -power_supply-objs += power_supply_leds.o -endif - -ifeq ($(CONFIG_POWER_SUPPLY_DEBUG),y) -EXTRA_CFLAGS += -DDEBUG -endif +power_supply-y := power_supply_core.o +power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o +power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o obj-$(CONFIG_POWER_SUPPLY) += power_supply.o @@ -29,6 +21,7 @@ obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o +obj-$(CONFIG_BATTERY_BQ20Z75) += bq20z75.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o @@ -37,3 +30,5 @@ obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o +obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o +obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c new file mode 100644 index 00000000000..492da27e1a4 --- /dev/null +++ b/drivers/power/bq20z75.c @@ -0,0 +1,493 @@ +/* + * Gas Gauge driver for TI's BQ20Z75 + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/power_supply.h> +#include <linux/i2c.h> +#include <linux/slab.h> + +enum { + REG_MANUFACTURER_DATA, + REG_TEMPERATURE, + REG_VOLTAGE, + REG_CURRENT, + REG_CAPACITY, + REG_TIME_TO_EMPTY, + REG_TIME_TO_FULL, + REG_STATUS, + REG_CYCLE_COUNT, + REG_SERIAL_NUMBER, + REG_REMAINING_CAPACITY, + REG_FULL_CHARGE_CAPACITY, + REG_DESIGN_CAPACITY, + REG_DESIGN_VOLTAGE, +}; + +/* manufacturer access defines */ +#define MANUFACTURER_ACCESS_STATUS 0x0006 +#define MANUFACTURER_ACCESS_SLEEP 0x0011 + +/* battery status value bits */ +#define BATTERY_DISCHARGING 0x40 +#define BATTERY_FULL_CHARGED 0x20 +#define BATTERY_FULL_DISCHARGED 0x10 + +#define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \ + .psp = _psp, \ + .addr = _addr, \ + .min_value = _min_value, \ + .max_value = _max_value, \ +} + +static const struct bq20z75_device_data { + enum power_supply_property psp; + u8 addr; + int min_value; + int max_value; +} bq20z75_data[] = { + [REG_MANUFACTURER_DATA] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535), + [REG_TEMPERATURE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535), + [REG_VOLTAGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000), + [REG_CURRENT] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, + 32767), + [REG_CAPACITY] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), + [REG_REMAINING_CAPACITY] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), + [REG_FULL_CHARGE_CAPACITY] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), + [REG_TIME_TO_EMPTY] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, + 65535), + [REG_TIME_TO_FULL] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, + 65535), + [REG_STATUS] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), + [REG_CYCLE_COUNT] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535), + [REG_DESIGN_CAPACITY] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, + 65535), + [REG_DESIGN_VOLTAGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, + 65535), + [REG_SERIAL_NUMBER] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), +}; + +static enum power_supply_property bq20z75_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + POWER_SUPPLY_PROP_SERIAL_NUMBER, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, +}; + +struct bq20z75_info { + struct i2c_client *client; + struct power_supply power_supply; +}; + +static int bq20z75_read_word_data(struct i2c_client *client, u8 address) +{ + s32 ret; + + ret = i2c_smbus_read_word_data(client, address); + if (ret < 0) { + dev_err(&client->dev, + "%s: i2c read at address 0x%x failed\n", + __func__, address); + return ret; + } + return le16_to_cpu(ret); +} + +static int bq20z75_write_word_data(struct i2c_client *client, u8 address, + u16 value) +{ + s32 ret; + + ret = i2c_smbus_write_word_data(client, address, le16_to_cpu(value)); + if (ret < 0) { + dev_err(&client->dev, + "%s: i2c write to address 0x%x failed\n", + __func__, address); + return ret; + } + return 0; +} + +static int bq20z75_get_battery_presence_and_health( + struct i2c_client *client, enum power_supply_property psp, + union power_supply_propval *val) +{ + s32 ret; + + /* Write to ManufacturerAccess with + * ManufacturerAccess command and then + * read the status */ + ret = bq20z75_write_word_data(client, + bq20z75_data[REG_MANUFACTURER_DATA].addr, + MANUFACTURER_ACCESS_STATUS); + if (ret < 0) + return ret; + + + ret = bq20z75_read_word_data(client, + bq20z75_data[REG_MANUFACTURER_DATA].addr); + if (ret < 0) + return ret; + + if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || + ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { + val->intval = 0; + return 0; + } + + /* Mask the upper nibble of 2nd byte and + * lower byte of response then + * shift the result by 8 to get status*/ + ret &= 0x0F00; + ret >>= 8; + if (psp == POWER_SUPPLY_PROP_PRESENT) { + if (ret == 0x0F) + /* battery removed */ + val->intval = 0; + else + val->intval = 1; + } else if (psp == POWER_SUPPLY_PROP_HEALTH) { + if (ret == 0x09) + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + else if (ret == 0x0B) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (ret == 0x0C) + val->intval = POWER_SUPPLY_HEALTH_DEAD; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + } + + return 0; +} + +static int bq20z75_get_battery_property(struct i2c_client *client, + int reg_offset, enum power_supply_property psp, + union power_supply_propval *val) +{ + s32 ret; + + ret = bq20z75_read_word_data(client, + bq20z75_data[reg_offset].addr); + if (ret < 0) + return ret; + + /* returned values are 16 bit */ + if (bq20z75_data[reg_offset].min_value < 0) + ret = (s16)ret; + + if (ret >= bq20z75_data[reg_offset].min_value && + ret <= bq20z75_data[reg_offset].max_value) { + val->intval = ret; + if (psp == POWER_SUPPLY_PROP_STATUS) { + if (ret & BATTERY_FULL_CHARGED) + val->intval = POWER_SUPPLY_STATUS_FULL; + else if (ret & BATTERY_FULL_DISCHARGED) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (ret & BATTERY_DISCHARGING) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; + } + } else { + if (psp == POWER_SUPPLY_PROP_STATUS) + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + else + val->intval = 0; + } + + return 0; +} + +static void bq20z75_unit_adjustment(struct i2c_client *client, + enum power_supply_property psp, union power_supply_propval *val) +{ +#define BASE_UNIT_CONVERSION 1000 +#define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) +#define TIME_UNIT_CONVERSION 600 +#define TEMP_KELVIN_TO_CELCIUS 2731 + switch (psp) { + case POWER_SUPPLY_PROP_ENERGY_NOW: + case POWER_SUPPLY_PROP_ENERGY_FULL: + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + val->intval *= BATTERY_MODE_CAP_MULT_WATT; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval *= BASE_UNIT_CONVERSION; + break; + + case POWER_SUPPLY_PROP_TEMP: + /* bq20z75 provides battery tempreture in 0.1°K + * so convert it to 0.1°C */ + val->intval -= TEMP_KELVIN_TO_CELCIUS; + val->intval *= 10; + break; + + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + val->intval *= TIME_UNIT_CONVERSION; + break; + + default: + dev_dbg(&client->dev, + "%s: no need for unit conversion %d\n", __func__, psp); + } +} + +static int bq20z75_get_battery_capacity(struct i2c_client *client, + int reg_offset, enum power_supply_property psp, + union power_supply_propval *val) +{ + s32 ret; + + ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); + if (ret < 0) + return ret; + + if (psp == POWER_SUPPLY_PROP_CAPACITY) { + /* bq20z75 spec says that this can be >100 % + * even if max value is 100 % */ + val->intval = min(ret, 100); + } else + val->intval = ret; + + return 0; +} + +static char bq20z75_serial[5]; +static int bq20z75_get_battery_serial_number(struct i2c_client *client, + union power_supply_propval *val) +{ + int ret; + + ret = bq20z75_read_word_data(client, + bq20z75_data[REG_SERIAL_NUMBER].addr); + if (ret < 0) + return ret; + + ret = sprintf(bq20z75_serial, "%04x", ret); + val->strval = bq20z75_serial; + + return 0; +} + +static int bq20z75_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int count; + int ret; + struct bq20z75_info *bq20z75_device = container_of(psy, + struct bq20z75_info, power_supply); + struct i2c_client *client = bq20z75_device->client; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_HEALTH: + ret = bq20z75_get_battery_presence_and_health(client, psp, val); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + + case POWER_SUPPLY_PROP_ENERGY_NOW: + case POWER_SUPPLY_PROP_ENERGY_FULL: + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + case POWER_SUPPLY_PROP_CAPACITY: + for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { + if (psp == bq20z75_data[count].psp) + break; + } + + ret = bq20z75_get_battery_capacity(client, count, psp, val); + if (ret) + return ret; + + break; + + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + ret = bq20z75_get_battery_serial_number(client, val); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { + if (psp == bq20z75_data[count].psp) + break; + } + + ret = bq20z75_get_battery_property(client, count, psp, val); + if (ret) + return ret; + + break; + + default: + dev_err(&client->dev, + "%s: INVALID property\n", __func__); + return -EINVAL; + } + + /* Convert units to match requirements for power supply class */ + bq20z75_unit_adjustment(client, psp, val); + + dev_dbg(&client->dev, + "%s: property = %d, value = %d\n", __func__, psp, val->intval); + + return 0; +} + +static int bq20z75_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bq20z75_info *bq20z75_device; + int rc; + + bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); + if (!bq20z75_device) + return -ENOMEM; + + bq20z75_device->client = client; + bq20z75_device->power_supply.name = "battery"; + bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; + bq20z75_device->power_supply.properties = bq20z75_properties; + bq20z75_device->power_supply.num_properties = + ARRAY_SIZE(bq20z75_properties); + bq20z75_device->power_supply.get_property = bq20z75_get_property; + + i2c_set_clientdata(client, bq20z75_device); + + rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); + if (rc) { + dev_err(&client->dev, + "%s: Failed to register power supply\n", __func__); + kfree(bq20z75_device); + return rc; + } + + dev_info(&client->dev, + "%s: battery gas gauge device registered\n", client->name); + + return 0; +} + +static int bq20z75_remove(struct i2c_client *client) +{ + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + + power_supply_unregister(&bq20z75_device->power_supply); + kfree(bq20z75_device); + bq20z75_device = NULL; + + return 0; +} + +#if defined CONFIG_PM +static int bq20z75_suspend(struct i2c_client *client, + pm_message_t state) +{ + s32 ret; + + /* write to manufacturer access with sleep command */ + ret = bq20z75_write_word_data(client, + bq20z75_data[REG_MANUFACTURER_DATA].addr, + MANUFACTURER_ACCESS_SLEEP); + if (ret < 0) + return ret; + + return 0; +} +#else +#define bq20z75_suspend NULL +#endif +/* any smbus transaction will wake up bq20z75 */ +#define bq20z75_resume NULL + +static const struct i2c_device_id bq20z75_id[] = { + { "bq20z75", 0 }, + {} +}; + +static struct i2c_driver bq20z75_battery_driver = { + .probe = bq20z75_probe, + .remove = bq20z75_remove, + .suspend = bq20z75_suspend, + .resume = bq20z75_resume, + .id_table = bq20z75_id, + .driver = { + .name = "bq20z75-battery", + }, +}; + +static int __init bq20z75_battery_init(void) +{ + return i2c_add_driver(&bq20z75_battery_driver); +} +module_init(bq20z75_battery_init); + +static void __exit bq20z75_battery_exit(void) +{ + i2c_del_driver(&bq20z75_battery_driver); +} +module_exit(bq20z75_battery_exit); + +MODULE_DESCRIPTION("BQ20z75 battery monitor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 3ec9c6a8896..eff0273d403 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -418,6 +418,7 @@ static int bq27x00_battery_remove(struct i2c_client *client) power_supply_unregister(&di->bat); + kfree(di->bus); kfree(di->bat.name); mutex_lock(&battery_mutex); diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index 4d3b27228a2..b3c01c16a16 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c @@ -586,6 +586,7 @@ static int ds2760_battery_remove(struct platform_device *pdev) &di->set_charged_work); destroy_workqueue(di->monitor_wqueue); power_supply_unregister(&di->bat); + kfree(di); return 0; } diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c index 84d3c43cf2b..6957e8af644 100644 --- a/drivers/power/ds2782_battery.c +++ b/drivers/power/ds2782_battery.c @@ -44,8 +44,8 @@ struct ds278x_info; struct ds278x_battery_ops { int (*get_battery_current)(struct ds278x_info *info, int *current_uA); - int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uA); - int (*get_battery_capacity)(struct ds278x_info *info, int *capacity_uA); + int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV); + int (*get_battery_capacity)(struct ds278x_info *info, int *capacity); }; #define to_ds278x_info(x) container_of(x, struct ds278x_info, battery) @@ -137,7 +137,7 @@ static int ds2782_get_current(struct ds278x_info *info, int *current_uA) return 0; } -static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uA) +static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uV) { s16 raw; int err; @@ -149,7 +149,7 @@ static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uA) err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); if (err) return err; - *voltage_uA = (raw / 32) * 4800; + *voltage_uV = (raw / 32) * 4800; return 0; } @@ -177,7 +177,7 @@ static int ds2786_get_current(struct ds278x_info *info, int *current_uA) return 0; } -static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA) +static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uV) { s16 raw; int err; @@ -189,7 +189,7 @@ static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA) err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); if (err) return err; - *voltage_uA = (raw / 8) * 1220; + *voltage_uV = (raw / 8) * 1220; return 0; } diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c new file mode 100644 index 00000000000..72512185f3e --- /dev/null +++ b/drivers/power/isp1704_charger.c @@ -0,0 +1,369 @@ +/* + * ISP1704 USB Charger Detection driver + * + * Copyright (C) 2010 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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/err.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/delay.h> + +#include <linux/usb/otg.h> +#include <linux/usb/ulpi.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +/* Vendor specific Power Control register */ +#define ISP1704_PWR_CTRL 0x3d +#define ISP1704_PWR_CTRL_SWCTRL (1 << 0) +#define ISP1704_PWR_CTRL_DET_COMP (1 << 1) +#define ISP1704_PWR_CTRL_BVALID_RISE (1 << 2) +#define ISP1704_PWR_CTRL_BVALID_FALL (1 << 3) +#define ISP1704_PWR_CTRL_DP_WKPU_EN (1 << 4) +#define ISP1704_PWR_CTRL_VDAT_DET (1 << 5) +#define ISP1704_PWR_CTRL_DPVSRC_EN (1 << 6) +#define ISP1704_PWR_CTRL_HWDETECT (1 << 7) + +#define NXP_VENDOR_ID 0x04cc + +static u16 isp170x_id[] = { + 0x1704, + 0x1707, +}; + +struct isp1704_charger { + struct device *dev; + struct power_supply psy; + struct otg_transceiver *otg; + struct notifier_block nb; + struct work_struct work; + + char model[7]; + unsigned present:1; +}; + +/* + * ISP1704 detects PS/2 adapters as charger. To make sure the detected charger + * is actually a dedicated charger, the following steps need to be taken. + */ +static inline int isp1704_charger_verify(struct isp1704_charger *isp) +{ + int ret = 0; + u8 r; + + /* Reset the transceiver */ + r = otg_io_read(isp->otg, ULPI_FUNC_CTRL); + r |= ULPI_FUNC_CTRL_RESET; + otg_io_write(isp->otg, ULPI_FUNC_CTRL, r); + usleep_range(1000, 2000); + + /* Set normal mode */ + r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK); + otg_io_write(isp->otg, ULPI_FUNC_CTRL, r); + + /* Clear the DP and DM pull-down bits */ + r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN; + otg_io_write(isp->otg, ULPI_CLR(ULPI_OTG_CTRL), r); + + /* Enable strong pull-up on DP (1.5K) and reset */ + r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET; + otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL), r); + usleep_range(1000, 2000); + + /* Read the line state */ + if (!otg_io_read(isp->otg, ULPI_DEBUG)) { + /* Disable strong pull-up on DP (1.5K) */ + otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL), + ULPI_FUNC_CTRL_TERMSELECT); + return 1; + } + + /* Is it a charger or PS/2 connection */ + + /* Enable weak pull-up resistor on DP */ + otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL), + ISP1704_PWR_CTRL_DP_WKPU_EN); + + /* Disable strong pull-up on DP (1.5K) */ + otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL), + ULPI_FUNC_CTRL_TERMSELECT); + + /* Enable weak pull-down resistor on DM */ + otg_io_write(isp->otg, ULPI_SET(ULPI_OTG_CTRL), + ULPI_OTG_CTRL_DM_PULLDOWN); + + /* It's a charger if the line states are clear */ + if (!(otg_io_read(isp->otg, ULPI_DEBUG))) + ret = 1; + + /* Disable weak pull-up resistor on DP */ + otg_io_write(isp->otg, ULPI_CLR(ISP1704_PWR_CTRL), + ISP1704_PWR_CTRL_DP_WKPU_EN); + + return ret; +} + +static inline int isp1704_charger_detect(struct isp1704_charger *isp) +{ + unsigned long timeout; + u8 r; + int ret = 0; + + /* set SW control bit in PWR_CTRL register */ + otg_io_write(isp->otg, ISP1704_PWR_CTRL, + ISP1704_PWR_CTRL_SWCTRL); + + /* enable manual charger detection */ + r = (ISP1704_PWR_CTRL_SWCTRL | ISP1704_PWR_CTRL_DPVSRC_EN); + otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL), r); + usleep_range(1000, 2000); + + timeout = jiffies + msecs_to_jiffies(300); + do { + /* Check if there is a charger */ + if (otg_io_read(isp->otg, ISP1704_PWR_CTRL) + & ISP1704_PWR_CTRL_VDAT_DET) { + ret = isp1704_charger_verify(isp); + break; + } + } while (!time_after(jiffies, timeout)); + + return ret; +} + +static void isp1704_charger_work(struct work_struct *data) +{ + int detect; + struct isp1704_charger *isp = + container_of(data, struct isp1704_charger, work); + + /* + * FIXME Only supporting dedicated chargers even though isp1704 can + * detect HUB and HOST chargers. If the device has already been + * enumerated, the detection will break the connection. + */ + if (isp->otg->state != OTG_STATE_B_IDLE) + return; + + /* disable data pullups */ + if (isp->otg->gadget) + usb_gadget_disconnect(isp->otg->gadget); + + /* detect charger */ + detect = isp1704_charger_detect(isp); + if (detect) { + isp->present = detect; + power_supply_changed(&isp->psy); + } + + /* enable data pullups */ + if (isp->otg->gadget) + usb_gadget_connect(isp->otg->gadget); +} + +static int isp1704_notifier_call(struct notifier_block *nb, + unsigned long event, void *unused) +{ + struct isp1704_charger *isp = + container_of(nb, struct isp1704_charger, nb); + + switch (event) { + case USB_EVENT_VBUS: + schedule_work(&isp->work); + break; + case USB_EVENT_NONE: + if (isp->present) { + isp->present = 0; + power_supply_changed(&isp->psy); + } + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static int isp1704_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct isp1704_charger *isp = + container_of(psy, struct isp1704_charger, psy); + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = isp->present; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = isp->model; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = "NXP"; + break; + default: + return -EINVAL; + } + return 0; +} + +static enum power_supply_property power_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static inline int isp1704_test_ulpi(struct isp1704_charger *isp) +{ + int vendor; + int product; + int i; + int ret = -ENODEV; + + /* Test ULPI interface */ + ret = otg_io_write(isp->otg, ULPI_SCRATCH, 0xaa); + if (ret < 0) + return ret; + + ret = otg_io_read(isp->otg, ULPI_SCRATCH); + if (ret < 0) + return ret; + + if (ret != 0xaa) + return -ENODEV; + + /* Verify the product and vendor id matches */ + vendor = otg_io_read(isp->otg, ULPI_VENDOR_ID_LOW); + vendor |= otg_io_read(isp->otg, ULPI_VENDOR_ID_HIGH) << 8; + if (vendor != NXP_VENDOR_ID) + return -ENODEV; + + product = otg_io_read(isp->otg, ULPI_PRODUCT_ID_LOW); + product |= otg_io_read(isp->otg, ULPI_PRODUCT_ID_HIGH) << 8; + + for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) { + if (product == isp170x_id[i]) { + sprintf(isp->model, "isp%x", product); + return product; + } + } + + dev_err(isp->dev, "product id %x not matching known ids", product); + + return -ENODEV; +} + +static int __devinit isp1704_charger_probe(struct platform_device *pdev) +{ + struct isp1704_charger *isp; + int ret = -ENODEV; + + isp = kzalloc(sizeof *isp, GFP_KERNEL); + if (!isp) + return -ENOMEM; + + isp->otg = otg_get_transceiver(); + if (!isp->otg) + goto fail0; + + ret = isp1704_test_ulpi(isp); + if (ret < 0) + goto fail1; + + isp->dev = &pdev->dev; + platform_set_drvdata(pdev, isp); + + isp->psy.name = "isp1704"; + isp->psy.type = POWER_SUPPLY_TYPE_USB; + isp->psy.properties = power_props; + isp->psy.num_properties = ARRAY_SIZE(power_props); + isp->psy.get_property = isp1704_charger_get_property; + + ret = power_supply_register(isp->dev, &isp->psy); + if (ret) + goto fail1; + + /* + * REVISIT: using work in order to allow the otg notifications to be + * made atomically in the future. + */ + INIT_WORK(&isp->work, isp1704_charger_work); + + isp->nb.notifier_call = isp1704_notifier_call; + + ret = otg_register_notifier(isp->otg, &isp->nb); + if (ret) + goto fail2; + + dev_info(isp->dev, "registered with product id %s\n", isp->model); + + return 0; +fail2: + power_supply_unregister(&isp->psy); +fail1: + otg_put_transceiver(isp->otg); +fail0: + kfree(isp); + + dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret); + + return ret; +} + +static int __devexit isp1704_charger_remove(struct platform_device *pdev) +{ + struct isp1704_charger *isp = platform_get_drvdata(pdev); + + otg_unregister_notifier(isp->otg, &isp->nb); + power_supply_unregister(&isp->psy); + otg_put_transceiver(isp->otg); + kfree(isp); + + return 0; +} + +static struct platform_driver isp1704_charger_driver = { + .driver = { + .name = "isp1704_charger", + }, + .probe = isp1704_charger_probe, + .remove = __devexit_p(isp1704_charger_remove), +}; + +static int __init isp1704_charger_init(void) +{ + return platform_driver_register(&isp1704_charger_driver); +} +module_init(isp1704_charger_init); + +static void __exit isp1704_charger_exit(void) +{ + platform_driver_unregister(&isp1704_charger_driver); +} +module_exit(isp1704_charger_exit); + +MODULE_ALIAS("platform:isp1704_charger"); +MODULE_AUTHOR("Nokia Corporation"); +MODULE_DESCRIPTION("ISP170x USB Charger driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c index 20c4b952e9b..a8108a73593 100644 --- a/drivers/power/jz4740-battery.c +++ b/drivers/power/jz4740-battery.c @@ -383,6 +383,7 @@ static int __devexit jz_battery_remove(struct platform_device *pdev) iounmap(jz_battery->base); release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem)); + kfree(jz_battery); return 0; } diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index aafc1c506ed..5bc1dcf7785 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -271,14 +271,14 @@ static int olpc_bat_get_property(struct power_supply *psy, if (ret) return ret; - val->intval = (int)be16_to_cpu(ec_word) * 9760L / 32; + val->intval = (s16)be16_to_cpu(ec_word) * 9760L / 32; break; case POWER_SUPPLY_PROP_CURRENT_AVG: ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2); if (ret) return ret; - val->intval = (int)be16_to_cpu(ec_word) * 15625L / 120; + val->intval = (s16)be16_to_cpu(ec_word) * 15625L / 120; break; case POWER_SUPPLY_PROP_CAPACITY: ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1); @@ -299,7 +299,7 @@ static int olpc_bat_get_property(struct power_supply *psy, if (ret) return ret; - val->intval = (int)be16_to_cpu(ec_word) * 100 / 256; + val->intval = (s16)be16_to_cpu(ec_word) * 100 / 256; break; case POWER_SUPPLY_PROP_TEMP_AMBIENT: ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2); @@ -313,7 +313,7 @@ static int olpc_bat_get_property(struct power_supply *psy, if (ret) return ret; - val->intval = (int)be16_to_cpu(ec_word) * 6250 / 15; + val->intval = (s16)be16_to_cpu(ec_word) * 6250 / 15; break; case POWER_SUPPLY_PROP_SERIAL_NUMBER: ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8); diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c index 066f994e6fe..4fa52e1781a 100644 --- a/drivers/power/pcf50633-charger.c +++ b/drivers/power/pcf50633-charger.c @@ -456,6 +456,7 @@ static int __devexit pcf50633_mbc_remove(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++) pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]); + sysfs_remove_group(&pdev->dev.kobj, &mbc_attr_group); power_supply_unregister(&mbc->usb); power_supply_unregister(&mbc->adapter); power_supply_unregister(&mbc->ac); diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 9d30eeb8c81..cd1f90754a3 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -42,7 +42,8 @@ static ssize_t power_supply_show_property(struct device *dev, struct device_attribute *attr, char *buf) { static char *type_text[] = { - "Battery", "UPS", "Mains", "USB" + "Battery", "UPS", "Mains", "USB", + "USB_DCP", "USB_CDP", "USB_ACA" }; static char *status_text[] = { "Unknown", "Charging", "Discharging", "Not charging", "Full" @@ -138,6 +139,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(voltage_min_design), POWER_SUPPLY_ATTR(voltage_now), POWER_SUPPLY_ATTR(voltage_avg), + POWER_SUPPLY_ATTR(current_max), POWER_SUPPLY_ATTR(current_now), POWER_SUPPLY_ATTR(current_avg), POWER_SUPPLY_ATTR(power_now), diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c new file mode 100644 index 00000000000..ff1f42398a2 --- /dev/null +++ b/drivers/power/twl4030_charger.c @@ -0,0 +1,565 @@ +/* + * TWL4030/TPS65950 BCI (Battery Charger Interface) driver + * + * Copyright (C) 2010 Gražvydas Ignotas <notasas@gmail.com> + * + * based on twl4030_bci_battery.c by TI + * Copyright (C) 2008 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/i2c/twl.h> +#include <linux/power_supply.h> +#include <linux/notifier.h> +#include <linux/usb/otg.h> + +#define TWL4030_BCIMSTATEC 0x02 +#define TWL4030_BCIICHG 0x08 +#define TWL4030_BCIVAC 0x0a +#define TWL4030_BCIVBUS 0x0c +#define TWL4030_BCIMFSTS4 0x10 +#define TWL4030_BCICTL1 0x23 + +#define TWL4030_BCIAUTOWEN BIT(5) +#define TWL4030_CONFIG_DONE BIT(4) +#define TWL4030_BCIAUTOUSB BIT(1) +#define TWL4030_BCIAUTOAC BIT(0) +#define TWL4030_CGAIN BIT(5) +#define TWL4030_USBFASTMCHG BIT(2) +#define TWL4030_STS_VBUS BIT(7) +#define TWL4030_STS_USB_ID BIT(2) + +/* BCI interrupts */ +#define TWL4030_WOVF BIT(0) /* Watchdog overflow */ +#define TWL4030_TMOVF BIT(1) /* Timer overflow */ +#define TWL4030_ICHGHIGH BIT(2) /* Battery charge current high */ +#define TWL4030_ICHGLOW BIT(3) /* Battery cc. low / FSM state change */ +#define TWL4030_ICHGEOC BIT(4) /* Battery current end-of-charge */ +#define TWL4030_TBATOR2 BIT(5) /* Battery temperature out of range 2 */ +#define TWL4030_TBATOR1 BIT(6) /* Battery temperature out of range 1 */ +#define TWL4030_BATSTS BIT(7) /* Battery status */ + +#define TWL4030_VBATLVL BIT(0) /* VBAT level */ +#define TWL4030_VBATOV BIT(1) /* VBAT overvoltage */ +#define TWL4030_VBUSOV BIT(2) /* VBUS overvoltage */ +#define TWL4030_ACCHGOV BIT(3) /* Ac charger overvoltage */ + +#define TWL4030_MSTATEC_USB BIT(4) +#define TWL4030_MSTATEC_AC BIT(5) +#define TWL4030_MSTATEC_MASK 0x0f +#define TWL4030_MSTATEC_QUICK1 0x02 +#define TWL4030_MSTATEC_QUICK7 0x07 +#define TWL4030_MSTATEC_COMPLETE1 0x0b +#define TWL4030_MSTATEC_COMPLETE4 0x0e + +static bool allow_usb; +module_param(allow_usb, bool, 1); +MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current"); + +struct twl4030_bci { + struct device *dev; + struct power_supply ac; + struct power_supply usb; + struct otg_transceiver *transceiver; + struct notifier_block otg_nb; + int irq_chg; + int irq_bci; +}; + +/* + * clear and set bits on an given register on a given module + */ +static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg) +{ + u8 val = 0; + int ret; + + ret = twl_i2c_read_u8(mod_no, &val, reg); + if (ret) + return ret; + + val &= ~clear; + val |= set; + + return twl_i2c_write_u8(mod_no, val, reg); +} + +static int twl4030_bci_read(u8 reg, u8 *val) +{ + return twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, val, reg); +} + +static int twl4030_clear_set_boot_bci(u8 clear, u8 set) +{ + return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, 0, + TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set, + TWL4030_PM_MASTER_BOOT_BCI); +} + +static int twl4030bci_read_adc_val(u8 reg) +{ + int ret, temp; + u8 val; + + /* read MSB */ + ret = twl4030_bci_read(reg + 1, &val); + if (ret) + return ret; + + temp = (int)(val & 0x03) << 8; + + /* read LSB */ + ret = twl4030_bci_read(reg, &val); + if (ret) + return ret; + + return temp | val; +} + +/* + * Check if VBUS power is present + */ +static int twl4030_bci_have_vbus(struct twl4030_bci *bci) +{ + int ret; + u8 hwsts; + + ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts, + TWL4030_PM_MASTER_STS_HW_CONDITIONS); + if (ret < 0) + return 0; + + dev_dbg(bci->dev, "check_vbus: HW_CONDITIONS %02x\n", hwsts); + + /* in case we also have STS_USB_ID, VBUS is driven by TWL itself */ + if ((hwsts & TWL4030_STS_VBUS) && !(hwsts & TWL4030_STS_USB_ID)) + return 1; + + return 0; +} + +/* + * Enable/Disable USB Charge funtionality. + */ +static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) +{ + int ret; + + if (enable) { + /* Check for USB charger conneted */ + if (!twl4030_bci_have_vbus(bci)) + return -ENODEV; + + /* + * Until we can find out what current the device can provide, + * require a module param to enable USB charging. + */ + if (!allow_usb) { + dev_warn(bci->dev, "USB charging is disabled.\n"); + return -EACCES; + } + + /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */ + ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB); + if (ret < 0) + return ret; + + /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */ + ret = twl4030_clear_set(TWL4030_MODULE_MAIN_CHARGE, 0, + TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4); + } else { + ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0); + } + + return ret; +} + +/* + * Enable/Disable AC Charge funtionality. + */ +static int twl4030_charger_enable_ac(bool enable) +{ + int ret; + + if (enable) + ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC); + else + ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0); + + return ret; +} + +/* + * TWL4030 CHG_PRES (AC charger presence) events + */ +static irqreturn_t twl4030_charger_interrupt(int irq, void *arg) +{ + struct twl4030_bci *bci = arg; + + dev_dbg(bci->dev, "CHG_PRES irq\n"); + power_supply_changed(&bci->ac); + power_supply_changed(&bci->usb); + + return IRQ_HANDLED; +} + +/* + * TWL4030 BCI monitoring events + */ +static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) +{ + struct twl4030_bci *bci = arg; + u8 irqs1, irqs2; + int ret; + + ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs1, + TWL4030_INTERRUPTS_BCIISR1A); + if (ret < 0) + return IRQ_HANDLED; + + ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs2, + TWL4030_INTERRUPTS_BCIISR2A); + if (ret < 0) + return IRQ_HANDLED; + + dev_dbg(bci->dev, "BCI irq %02x %02x\n", irqs2, irqs1); + + if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) { + /* charger state change, inform the core */ + power_supply_changed(&bci->ac); + power_supply_changed(&bci->usb); + } + + /* various monitoring events, for now we just log them here */ + if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1)) + dev_warn(bci->dev, "battery temperature out of range\n"); + + if (irqs1 & TWL4030_BATSTS) + dev_crit(bci->dev, "battery disconnected\n"); + + if (irqs2 & TWL4030_VBATOV) + dev_crit(bci->dev, "VBAT overvoltage\n"); + + if (irqs2 & TWL4030_VBUSOV) + dev_crit(bci->dev, "VBUS overvoltage\n"); + + if (irqs2 & TWL4030_ACCHGOV) + dev_crit(bci->dev, "Ac charger overvoltage\n"); + + return IRQ_HANDLED; +} + +static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, + void *priv) +{ + struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, otg_nb); + + dev_dbg(bci->dev, "OTG notify %lu\n", val); + + switch (val) { + case USB_EVENT_VBUS: + case USB_EVENT_CHARGER: + twl4030_charger_enable_usb(bci, true); + break; + case USB_EVENT_NONE: + twl4030_charger_enable_usb(bci, false); + break; + } + + return NOTIFY_OK; +} + +/* + * TI provided formulas: + * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85 + * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7 + * Here we use integer approximation of: + * CGAIN == 0: val * 1.6618 - 0.85 + * CGAIN == 1: (val * 1.6618 - 0.85) * 2 + */ +static int twl4030_charger_get_current(void) +{ + int curr; + int ret; + u8 bcictl1; + + curr = twl4030bci_read_adc_val(TWL4030_BCIICHG); + if (curr < 0) + return curr; + + ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1); + if (ret) + return ret; + + ret = (curr * 16618 - 850 * 10000) / 10; + if (bcictl1 & TWL4030_CGAIN) + ret *= 2; + + return ret; +} + +/* + * Returns the main charge FSM state + * Or < 0 on failure. + */ +static int twl4030bci_state(struct twl4030_bci *bci) +{ + int ret; + u8 state; + + ret = twl4030_bci_read(TWL4030_BCIMSTATEC, &state); + if (ret) { + pr_err("twl4030_bci: error reading BCIMSTATEC\n"); + return ret; + } + + dev_dbg(bci->dev, "state: %02x\n", state); + + return state; +} + +static int twl4030_bci_state_to_status(int state) +{ + state &= TWL4030_MSTATEC_MASK; + if (TWL4030_MSTATEC_QUICK1 <= state && state <= TWL4030_MSTATEC_QUICK7) + return POWER_SUPPLY_STATUS_CHARGING; + else if (TWL4030_MSTATEC_COMPLETE1 <= state && + state <= TWL4030_MSTATEC_COMPLETE4) + return POWER_SUPPLY_STATUS_FULL; + else + return POWER_SUPPLY_STATUS_NOT_CHARGING; +} + +static int twl4030_bci_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct twl4030_bci *bci = dev_get_drvdata(psy->dev->parent); + int is_charging; + int state; + int ret; + + state = twl4030bci_state(bci); + if (state < 0) + return state; + + if (psy->type == POWER_SUPPLY_TYPE_USB) + is_charging = state & TWL4030_MSTATEC_USB; + else + is_charging = state & TWL4030_MSTATEC_AC; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (is_charging) + val->intval = twl4030_bci_state_to_status(state); + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + /* charging must be active for meaningful result */ + if (!is_charging) + return -ENODATA; + if (psy->type == POWER_SUPPLY_TYPE_USB) { + ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS); + if (ret < 0) + return ret; + /* BCIVBUS uses ADCIN8, 7/1023 V/step */ + val->intval = ret * 6843; + } else { + ret = twl4030bci_read_adc_val(TWL4030_BCIVAC); + if (ret < 0) + return ret; + /* BCIVAC uses ADCIN11, 10/1023 V/step */ + val->intval = ret * 9775; + } + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + if (!is_charging) + return -ENODATA; + /* current measurement is shared between AC and USB */ + ret = twl4030_charger_get_current(); + if (ret < 0) + return ret; + val->intval = ret; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = is_charging && + twl4030_bci_state_to_status(state) != + POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property twl4030_charger_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static int __init twl4030_bci_probe(struct platform_device *pdev) +{ + struct twl4030_bci *bci; + int ret; + int reg; + + bci = kzalloc(sizeof(*bci), GFP_KERNEL); + if (bci == NULL) + return -ENOMEM; + + bci->dev = &pdev->dev; + bci->irq_chg = platform_get_irq(pdev, 0); + bci->irq_bci = platform_get_irq(pdev, 1); + + platform_set_drvdata(pdev, bci); + + bci->ac.name = "twl4030_ac"; + bci->ac.type = POWER_SUPPLY_TYPE_MAINS; + bci->ac.properties = twl4030_charger_props; + bci->ac.num_properties = ARRAY_SIZE(twl4030_charger_props); + bci->ac.get_property = twl4030_bci_get_property; + + ret = power_supply_register(&pdev->dev, &bci->ac); + if (ret) { + dev_err(&pdev->dev, "failed to register ac: %d\n", ret); + goto fail_register_ac; + } + + bci->usb.name = "twl4030_usb"; + bci->usb.type = POWER_SUPPLY_TYPE_USB; + bci->usb.properties = twl4030_charger_props; + bci->usb.num_properties = ARRAY_SIZE(twl4030_charger_props); + bci->usb.get_property = twl4030_bci_get_property; + + ret = power_supply_register(&pdev->dev, &bci->usb); + if (ret) { + dev_err(&pdev->dev, "failed to register usb: %d\n", ret); + goto fail_register_usb; + } + + ret = request_threaded_irq(bci->irq_chg, NULL, + twl4030_charger_interrupt, 0, pdev->name, bci); + if (ret < 0) { + dev_err(&pdev->dev, "could not request irq %d, status %d\n", + bci->irq_chg, ret); + goto fail_chg_irq; + } + + ret = request_threaded_irq(bci->irq_bci, NULL, + twl4030_bci_interrupt, 0, pdev->name, bci); + if (ret < 0) { + dev_err(&pdev->dev, "could not request irq %d, status %d\n", + bci->irq_bci, ret); + goto fail_bci_irq; + } + + bci->transceiver = otg_get_transceiver(); + if (bci->transceiver != NULL) { + bci->otg_nb.notifier_call = twl4030_bci_usb_ncb; + otg_register_notifier(bci->transceiver, &bci->otg_nb); + } + + /* Enable interrupts now. */ + reg = ~(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 | + TWL4030_TBATOR1 | TWL4030_BATSTS); + ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg, + TWL4030_INTERRUPTS_BCIIMR1A); + if (ret < 0) { + dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret); + goto fail_unmask_interrupts; + } + + reg = ~(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV); + ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg, + TWL4030_INTERRUPTS_BCIIMR2A); + if (ret < 0) + dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret); + + twl4030_charger_enable_ac(true); + twl4030_charger_enable_usb(bci, true); + + return 0; + +fail_unmask_interrupts: + if (bci->transceiver != NULL) { + otg_unregister_notifier(bci->transceiver, &bci->otg_nb); + otg_put_transceiver(bci->transceiver); + } + free_irq(bci->irq_bci, bci); +fail_bci_irq: + free_irq(bci->irq_chg, bci); +fail_chg_irq: + power_supply_unregister(&bci->usb); +fail_register_usb: + power_supply_unregister(&bci->ac); +fail_register_ac: + platform_set_drvdata(pdev, NULL); + kfree(bci); + + return ret; +} + +static int __exit twl4030_bci_remove(struct platform_device *pdev) +{ + struct twl4030_bci *bci = platform_get_drvdata(pdev); + + twl4030_charger_enable_ac(false); + twl4030_charger_enable_usb(bci, false); + + /* mask interrupts */ + twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, + TWL4030_INTERRUPTS_BCIIMR1A); + twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, + TWL4030_INTERRUPTS_BCIIMR2A); + + if (bci->transceiver != NULL) { + otg_unregister_notifier(bci->transceiver, &bci->otg_nb); + otg_put_transceiver(bci->transceiver); + } + free_irq(bci->irq_bci, bci); + free_irq(bci->irq_chg, bci); + power_supply_unregister(&bci->usb); + power_supply_unregister(&bci->ac); + platform_set_drvdata(pdev, NULL); + kfree(bci); + + return 0; +} + +static struct platform_driver twl4030_bci_driver = { + .driver = { + .name = "twl4030_bci", + .owner = THIS_MODULE, + }, + .remove = __exit_p(twl4030_bci_remove), +}; + +static int __init twl4030_bci_init(void) +{ + return platform_driver_probe(&twl4030_bci_driver, twl4030_bci_probe); +} +module_init(twl4030_bci_init); + +static void __exit twl4030_bci_exit(void) +{ + platform_driver_unregister(&twl4030_bci_driver); +} +module_exit(twl4030_bci_exit); + +MODULE_AUTHOR("Gražydas Ignotas"); +MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:twl4030_bci"); diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c index fbcc36dae47..ddf8cf5f320 100644 --- a/drivers/power/wm831x_power.c +++ b/drivers/power/wm831x_power.c @@ -267,7 +267,6 @@ static void wm831x_config_battery(struct wm831x *wm831x) ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1, WM831X_CHG_ENA_MASK | WM831X_CHG_FAST_MASK | - WM831X_CHG_ITERM_MASK | WM831X_CHG_ITERM_MASK, reg1); if (ret != 0) @@ -612,6 +611,7 @@ static __devexit int wm831x_power_remove(struct platform_device *pdev) power_supply_unregister(&wm831x_power->battery); power_supply_unregister(&wm831x_power->wall); power_supply_unregister(&wm831x_power->usb); + kfree(wm831x_power); return 0; } diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 48ca7132cc0..2785a0f16c9 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -765,15 +765,15 @@ config RTC_DRV_AT32AP700X AT32AP700x family processors. config RTC_DRV_AT91RM9200 - tristate "AT91RM9200 or AT91SAM9RL" - depends on ARCH_AT91RM9200 || ARCH_AT91SAM9RL + tristate "AT91RM9200 or some AT91SAM9 RTC" + depends on ARCH_AT91RM9200 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 help Driver for the internal RTC (Realtime Clock) module found on - Atmel AT91RM9200's and AT91SAM9RL chips. On SAM9RL chips + Atmel AT91RM9200's and some AT91SAM9 chips. On AT91SAM9 chips this is powered by the backup power supply. config RTC_DRV_AT91SAM9 - tristate "AT91SAM9x/AT91CAP9" + tristate "AT91SAM9x/AT91CAP9 RTT as RTC" depends on ARCH_AT91 && !(ARCH_AT91RM9200 || ARCH_AT91X40) help RTC driver for the Atmel AT91SAM9x and AT91CAP9 internal RTT @@ -781,8 +781,8 @@ config RTC_DRV_AT91SAM9 supply (such as a small coin cell battery), but do not need to be used as RTCs. - (On AT91SAM9rl chips you probably want to use the dedicated RTC - module and leave the RTT available for other uses.) + (On AT91SAM9rl and AT91SAM9G45 chips you probably want to use the + dedicated RTC module and leave the RTT available for other uses.) config RTC_DRV_AT91SAM9_RTT int diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 92781648439..aff9dcd051c 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1410,6 +1410,33 @@ config SERIAL_OF_PLATFORM Currently, only 8250 compatible ports are supported, but others can easily be added. +config SERIAL_OMAP + tristate "OMAP serial port support" + depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4 + select SERIAL_CORE + help + If you have a machine based on an Texas Instruments OMAP CPU you + can enable its onboard serial ports by enabling this option. + + By enabling this option you take advantage of dma feature available + with the omap-serial driver. DMA support can be enabled from platform + data. + +config SERIAL_OMAP_CONSOLE + bool "Console on OMAP serial port" + depends on SERIAL_OMAP + select SERIAL_CORE_CONSOLE + help + Select this option if you would like to use omap serial port as + console. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyOx". (Try "man bootparam" or see the documentation of + your boot loader about how to pass options to the kernel at + boot time.) + config SERIAL_OF_PLATFORM_NWPSERIAL tristate "NWP serial port driver" depends on PPC_OF && PPC_DCR diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 1ca4fd599ff..c5705765454 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -88,3 +88,4 @@ obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o +obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o diff --git a/drivers/serial/omap-serial.c b/drivers/serial/omap-serial.c new file mode 100644 index 00000000000..14365f72b66 --- /dev/null +++ b/drivers/serial/omap-serial.c @@ -0,0 +1,1333 @@ +/* + * Driver for OMAP-UART controller. + * Based on drivers/serial/8250.c + * + * Copyright (C) 2010 Texas Instruments. + * + * Authors: + * Govindraj R <govindraj.raja@ti.com> + * Thara Gopinath <thara@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Note: This driver is made seperate from 8250 driver as we cannot + * over load 8250 driver with omap platform specific configuration for + * features like DMA, it makes easier to implement features like DMA and + * hardware flow control and software flow control configuration with + * this driver as required for the omap-platform. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/serial_reg.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/serial_core.h> +#include <linux/irq.h> + +#include <plat/dma.h> +#include <plat/dmtimer.h> +#include <plat/omap-serial.h> + +static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; + +/* Forward declaration of functions */ +static void uart_tx_dma_callback(int lch, u16 ch_status, void *data); +static void serial_omap_rx_timeout(unsigned long uart_no); +static int serial_omap_start_rxdma(struct uart_omap_port *up); + +static inline unsigned int serial_in(struct uart_omap_port *up, int offset) +{ + offset <<= up->port.regshift; + return readw(up->port.membase + offset); +} + +static inline void serial_out(struct uart_omap_port *up, int offset, int value) +{ + offset <<= up->port.regshift; + writew(value, up->port.membase + offset); +} + +static inline void serial_omap_clear_fifos(struct uart_omap_port *up) +{ + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); +} + +/* + * serial_omap_get_divisor - calculate divisor value + * @port: uart port info + * @baud: baudrate for which divisor needs to be calculated. + * + * We have written our own function to get the divisor so as to support + * 13x mode. 3Mbps Baudrate as an different divisor. + * Reference OMAP TRM Chapter 17: + * Table 17-1. UART Mode Baud Rates, Divisor Values, and Error Rates + * referring to oversampling - divisor value + * baudrate 460,800 to 3,686,400 all have divisor 13 + * except 3,000,000 which has divisor value 16 + */ +static unsigned int +serial_omap_get_divisor(struct uart_port *port, unsigned int baud) +{ + unsigned int divisor; + + if (baud > OMAP_MODE13X_SPEED && baud != 3000000) + divisor = 13; + else + divisor = 16; + return port->uartclk/(baud * divisor); +} + +static void serial_omap_stop_rxdma(struct uart_omap_port *up) +{ + if (up->uart_dma.rx_dma_used) { + del_timer(&up->uart_dma.rx_timer); + omap_stop_dma(up->uart_dma.rx_dma_channel); + omap_free_dma(up->uart_dma.rx_dma_channel); + up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; + up->uart_dma.rx_dma_used = false; + } +} + +static void serial_omap_enable_ms(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->pdev->id); + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +static void serial_omap_stop_tx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + if (up->use_dma && + up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) { + /* + * Check if dma is still active. If yes do nothing, + * return. Else stop dma + */ + if (omap_get_dma_active_status(up->uart_dma.tx_dma_channel)) + return; + omap_stop_dma(up->uart_dma.tx_dma_channel); + omap_free_dma(up->uart_dma.tx_dma_channel); + up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; + } + + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_omap_stop_rx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + if (up->use_dma) + serial_omap_stop_rxdma(up); + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static inline void receive_chars(struct uart_omap_port *up, int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned int flag; + unsigned char ch, lsr = *status; + int max_count = 256; + + do { + if (likely(lsr & UART_LSR_DR)) + ch = serial_in(up, UART_RX); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { + /* + * For statistics only + */ + if (lsr & UART_LSR_BI) { + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (lsr & UART_LSR_PE) { + up->port.icount.parity++; + } else if (lsr & UART_LSR_FE) { + up->port.icount.frame++; + } + + if (lsr & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + lsr &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_OMAP_CONSOLE + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + lsr |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + if (lsr & UART_LSR_BI) + flag = TTY_BREAK; + else if (lsr & UART_LSR_PE) + flag = TTY_PARITY; + else if (lsr & UART_LSR_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); +ignore_char: + lsr = serial_in(up, UART_LSR); + } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&up->port.lock); +} + +static void transmit_chars(struct uart_omap_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + serial_out(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + serial_omap_stop_tx(&up->port); + return; + } + count = up->port.fifosize / 4; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) + serial_omap_stop_tx(&up->port); +} + +static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) +{ + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_omap_start_tx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + struct circ_buf *xmit; + unsigned int start; + int ret = 0; + + if (!up->use_dma) { + serial_omap_enable_ier_thri(up); + return; + } + + if (up->uart_dma.tx_dma_used) + return; + + xmit = &up->port.state->xmit; + + if (up->uart_dma.tx_dma_channel == OMAP_UART_DMA_CH_FREE) { + ret = omap_request_dma(up->uart_dma.uart_dma_tx, + "UART Tx DMA", + (void *)uart_tx_dma_callback, up, + &(up->uart_dma.tx_dma_channel)); + + if (ret < 0) { + serial_omap_enable_ier_thri(up); + return; + } + } + spin_lock(&(up->uart_dma.tx_lock)); + up->uart_dma.tx_dma_used = true; + spin_unlock(&(up->uart_dma.tx_lock)); + + start = up->uart_dma.tx_buf_dma_phys + + (xmit->tail & (UART_XMIT_SIZE - 1)); + + up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); + /* + * It is a circular buffer. See if the buffer has wounded back. + * If yes it will have to be transferred in two separate dma + * transfers + */ + if (start + up->uart_dma.tx_buf_size >= + up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) + up->uart_dma.tx_buf_size = + (up->uart_dma.tx_buf_dma_phys + + UART_XMIT_SIZE) - start; + + omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, start, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.tx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_tx, 0); + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.tx_dma_channel); +} + +static unsigned int check_modem_status(struct uart_omap_port *up) +{ + unsigned int status; + + status = serial_in(up, UART_MSR); + status |= up->msr_saved_flags; + up->msr_saved_flags = 0; + if ((status & UART_MSR_ANY_DELTA) == 0) + return status; + + if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && + up->port.state != NULL) { + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change + (&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change + (&up->port, status & UART_MSR_CTS); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); + } + + return status; +} + +/** + * serial_omap_irq() - This handles the interrupt from one port + * @irq: uart port irq number + * @dev_id: uart port info + */ +static inline irqreturn_t serial_omap_irq(int irq, void *dev_id) +{ + struct uart_omap_port *up = dev_id; + unsigned int iir, lsr; + unsigned long flags; + + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) + return IRQ_NONE; + + spin_lock_irqsave(&up->port.lock, flags); + lsr = serial_in(up, UART_LSR); + if (iir & UART_IIR_RLSI) { + if (!up->use_dma) { + if (lsr & UART_LSR_DR) + receive_chars(up, &lsr); + } else { + up->ier &= ~(UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + if ((serial_omap_start_rxdma(up) != 0) && + (lsr & UART_LSR_DR)) + receive_chars(up, &lsr); + } + } + + check_modem_status(up); + if ((lsr & UART_LSR_THRE) && (iir & UART_IIR_THRI)) + transmit_chars(up); + + spin_unlock_irqrestore(&up->port.lock, flags); + up->port_activity = jiffies; + return IRQ_HANDLED; +} + +static unsigned int serial_omap_tx_empty(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + unsigned int ret = 0; + + dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->pdev->id); + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial_omap_get_mctrl(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char status; + unsigned int ret = 0; + + status = check_modem_status(up); + dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->pdev->id); + + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + return ret; +} + +static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char mcr = 0; + + dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->pdev->id); + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + mcr |= up->mcr; + serial_out(up, UART_MCR, mcr); +} + +static void serial_omap_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + + dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->pdev->id); + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int serial_omap_startup(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(up->port.irq, serial_omap_irq, up->port.irqflags, + up->name, up); + if (retval) + return retval; + + dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->pdev->id); + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial_omap_clear_fifos(up); + /* For Hardware flow control */ + serial_out(up, UART_MCR, UART_MCR_RTS); + + /* + * Clear the interrupt registers. + */ + (void) serial_in(up, UART_LSR); + if (serial_in(up, UART_LSR) & UART_LSR_DR) + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + /* + * Now, initialize the UART + */ + serial_out(up, UART_LCR, UART_LCR_WLEN8); + spin_lock_irqsave(&up->port.lock, flags); + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + up->port.mctrl |= TIOCM_OUT2; + serial_omap_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + up->msr_saved_flags = 0; + if (up->use_dma) { + free_page((unsigned long)up->port.state->xmit.buf); + up->port.state->xmit.buf = dma_alloc_coherent(NULL, + UART_XMIT_SIZE, + (dma_addr_t *)&(up->uart_dma.tx_buf_dma_phys), + 0); + init_timer(&(up->uart_dma.rx_timer)); + up->uart_dma.rx_timer.function = serial_omap_rx_timeout; + up->uart_dma.rx_timer.data = up->pdev->id; + /* Currently the buffer size is 4KB. Can increase it */ + up->uart_dma.rx_buf = dma_alloc_coherent(NULL, + up->uart_dma.rx_buf_size, + (dma_addr_t *)&(up->uart_dma.rx_buf_dma_phys), 0); + } + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + + up->port_activity = jiffies; + return 0; +} + +static void serial_omap_shutdown(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + + dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->pdev->id); + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_out(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + up->port.mctrl &= ~TIOCM_OUT2; + serial_omap_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); + serial_omap_clear_fifos(up); + + /* + * Read data port to reset things, and then free the irq + */ + if (serial_in(up, UART_LSR) & UART_LSR_DR) + (void) serial_in(up, UART_RX); + if (up->use_dma) { + dma_free_coherent(up->port.dev, + UART_XMIT_SIZE, up->port.state->xmit.buf, + up->uart_dma.tx_buf_dma_phys); + up->port.state->xmit.buf = NULL; + serial_omap_stop_rx(port); + dma_free_coherent(up->port.dev, + up->uart_dma.rx_buf_size, up->uart_dma.rx_buf, + up->uart_dma.rx_buf_dma_phys); + up->uart_dma.rx_buf = NULL; + } + free_irq(up->port.irq, up); +} + +static inline void +serial_omap_configure_xonxoff + (struct uart_omap_port *up, struct ktermios *termios) +{ + unsigned char efr = 0; + + up->lcr = serial_in(up, UART_LCR); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB); + + serial_out(up, UART_XON1, termios->c_cc[VSTART]); + serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]); + + /* clear SW control mode bits */ + efr = up->efr; + efr &= OMAP_UART_SW_CLR; + + /* + * IXON Flag: + * Enable XON/XOFF flow control on output. + * Transmit XON1, XOFF1 + */ + if (termios->c_iflag & IXON) + efr |= OMAP_UART_SW_TX; + + /* + * IXOFF Flag: + * Enable XON/XOFF flow control on input. + * Receiver compares XON1, XOFF1. + */ + if (termios->c_iflag & IXOFF) + efr |= OMAP_UART_SW_RX; + + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + serial_out(up, UART_LCR, UART_LCR_DLAB); + + up->mcr = serial_in(up, UART_MCR); + + /* + * IXANY Flag: + * Enable any character to restart output. + * Operation resumes after receiving any + * character after recognition of the XOFF character + */ + if (termios->c_iflag & IXANY) + up->mcr |= UART_MCR_XONANY; + + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); + /* Enable special char function UARTi.EFR_REG[5] and + * load the new software flow control mode IXON or IXOFF + * and restore the UARTi.EFR_REG[4] ENHANCED_EN value. + */ + serial_out(up, UART_EFR, efr | UART_EFR_SCD); + serial_out(up, UART_LCR, UART_LCR_DLAB); + + serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); + serial_out(up, UART_LCR, up->lcr); +} + +static void +serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char cval = 0; + unsigned char efr = 0; + unsigned long flags = 0; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * Ask the core to calculate the divisor for us. + */ + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/13); + quot = serial_omap_get_divisor(port, baud); + + up->fcr = UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01 | + UART_FCR_ENABLE_FIFO; + if (up->use_dma) + up->fcr |= UART_FCR_DMA_SELECT; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * Modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); + serial_out(up, UART_LCR, cval); /* reset DLAB */ + + /* FIFOs and DMA Settings */ + + /* FCR can be changed only when the + * baud clock is not running + * DLL_REG and DLH_REG set to 0. + */ + serial_out(up, UART_LCR, UART_LCR_DLAB); + serial_out(up, UART_DLL, 0); + serial_out(up, UART_DLM, 0); + serial_out(up, UART_LCR, 0); + + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_LCR, UART_LCR_DLAB); + up->mcr = serial_in(up, UART_MCR); + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + /* FIFO ENABLE, DMA MODE */ + serial_out(up, UART_FCR, up->fcr); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + + if (up->use_dma) { + serial_out(up, UART_TI752_TLR, 0); + serial_out(up, UART_OMAP_SCR, + (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8)); + } + + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, UART_LCR_DLAB); + serial_out(up, UART_MCR, up->mcr); + + /* Protocol, Baud Rate, and Interrupt Settings */ + + serial_out(up, UART_OMAP_MDR1, OMAP_MDR1_DISABLE); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_LCR, 0); + serial_out(up, UART_IER, 0); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + + serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ + + serial_out(up, UART_LCR, 0); + serial_out(up, UART_IER, up->ier); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, cval); + + if (baud > 230400 && baud != 3000000) + serial_out(up, UART_OMAP_MDR1, OMAP_MDR1_MODE13X); + else + serial_out(up, UART_OMAP_MDR1, OMAP_MDR1_MODE16X); + + /* Hardware Flow Control Configuration */ + + if (termios->c_cflag & CRTSCTS) { + efr |= (UART_EFR_CTS | UART_EFR_RTS); + serial_out(up, UART_LCR, UART_LCR_DLAB); + + up->mcr = serial_in(up, UART_MCR); + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); + serial_out(up, UART_EFR, efr); /* Enable AUTORTS and AUTOCTS */ + serial_out(up, UART_LCR, UART_LCR_DLAB); + serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS); + serial_out(up, UART_LCR, cval); + } + + serial_omap_set_mctrl(&up->port, up->port.mctrl); + /* Software Flow Control Configuration */ + if (termios->c_iflag & (IXON | IXOFF)) + serial_omap_configure_xonxoff(up, termios); + + spin_unlock_irqrestore(&up->port.lock, flags); + dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->pdev->id); +} + +static void +serial_omap_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char efr; + + dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->pdev->id); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, efr | UART_EFR_ECB); + serial_out(up, UART_LCR, 0); + + serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + serial_out(up, UART_EFR, efr); + serial_out(up, UART_LCR, 0); + /* Enable module level wake up */ + serial_out(up, UART_OMAP_WER, + (state != 0) ? OMAP_UART_WER_MOD_WKUP : 0); +} + +static void serial_omap_release_port(struct uart_port *port) +{ + dev_dbg(port->dev, "serial_omap_release_port+\n"); +} + +static int serial_omap_request_port(struct uart_port *port) +{ + dev_dbg(port->dev, "serial_omap_request_port+\n"); + return 0; +} + +static void serial_omap_config_port(struct uart_port *port, int flags) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_config_port+%d\n", + up->pdev->id); + up->port.type = PORT_OMAP; +} + +static int +serial_omap_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + dev_dbg(port->dev, "serial_omap_verify_port+\n"); + return -EINVAL; +} + +static const char * +serial_omap_type(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_type+%d\n", up->pdev->id); + return up->name; +} + +#ifdef CONFIG_SERIAL_OMAP_CONSOLE + +static struct uart_omap_port *serial_omap_console_ports[4]; + +static struct uart_driver serial_omap_reg; + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static inline void wait_for_xmitr(struct uart_omap_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while ((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + for (tmout = 1000000; tmout; tmout--) { + unsigned int msr = serial_in(up, UART_MSR); + + up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; + if (msr & UART_MSR_CTS) + break; + + udelay(1); + } + } +} + +static void serial_omap_console_putchar(struct uart_port *port, int ch) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + wait_for_xmitr(up); + serial_out(up, UART_TX, ch); +} + +static void +serial_omap_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_omap_port *up = serial_omap_console_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + local_irq_save(flags); + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&up->port.lock); + else + spin_lock(&up->port.lock); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + + uart_console_write(&up->port, s, count, serial_omap_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); + /* + * The receive handling will happen properly because the + * receive ready bit will still be set; it is not cleared + * on read. However, modem control will not, we must + * call it if we have saved something in the saved flags + * while processing with interrupts off. + */ + if (up->msr_saved_flags) + check_modem_status(up); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static int __init +serial_omap_console_setup(struct console *co, char *options) +{ + struct uart_omap_port *up; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (serial_omap_console_ports[co->index] == NULL) + return -ENODEV; + up = serial_omap_console_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&up->port, co, baud, parity, bits, flow); +} + +static struct console serial_omap_console = { + .name = OMAP_SERIAL_NAME, + .write = serial_omap_console_write, + .device = uart_console_device, + .setup = serial_omap_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &serial_omap_reg, +}; + +static void serial_omap_add_console_port(struct uart_omap_port *up) +{ + serial_omap_console_ports[up->pdev->id] = up; +} + +#define OMAP_CONSOLE (&serial_omap_console) + +#else + +#define OMAP_CONSOLE NULL + +static inline void serial_omap_add_console_port(struct uart_omap_port *up) +{} + +#endif + +static struct uart_ops serial_omap_pops = { + .tx_empty = serial_omap_tx_empty, + .set_mctrl = serial_omap_set_mctrl, + .get_mctrl = serial_omap_get_mctrl, + .stop_tx = serial_omap_stop_tx, + .start_tx = serial_omap_start_tx, + .stop_rx = serial_omap_stop_rx, + .enable_ms = serial_omap_enable_ms, + .break_ctl = serial_omap_break_ctl, + .startup = serial_omap_startup, + .shutdown = serial_omap_shutdown, + .set_termios = serial_omap_set_termios, + .pm = serial_omap_pm, + .type = serial_omap_type, + .release_port = serial_omap_release_port, + .request_port = serial_omap_request_port, + .config_port = serial_omap_config_port, + .verify_port = serial_omap_verify_port, +}; + +static struct uart_driver serial_omap_reg = { + .owner = THIS_MODULE, + .driver_name = "OMAP-SERIAL", + .dev_name = OMAP_SERIAL_NAME, + .nr = OMAP_MAX_HSUART_PORTS, + .cons = OMAP_CONSOLE, +}; + +static int +serial_omap_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct uart_omap_port *up = platform_get_drvdata(pdev); + + if (up) + uart_suspend_port(&serial_omap_reg, &up->port); + return 0; +} + +static int serial_omap_resume(struct platform_device *dev) +{ + struct uart_omap_port *up = platform_get_drvdata(dev); + + if (up) + uart_resume_port(&serial_omap_reg, &up->port); + return 0; +} + +static void serial_omap_rx_timeout(unsigned long uart_no) +{ + struct uart_omap_port *up = ui[uart_no]; + unsigned int curr_dma_pos, curr_transmitted_size; + int ret = 0; + + curr_dma_pos = omap_get_dma_dst_pos(up->uart_dma.rx_dma_channel); + if ((curr_dma_pos == up->uart_dma.prev_rx_dma_pos) || + (curr_dma_pos == 0)) { + if (jiffies_to_msecs(jiffies - up->port_activity) < + RX_TIMEOUT) { + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + } else { + serial_omap_stop_rxdma(up); + up->ier |= (UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + } + return; + } + + curr_transmitted_size = curr_dma_pos - + up->uart_dma.prev_rx_dma_pos; + up->port.icount.rx += curr_transmitted_size; + tty_insert_flip_string(up->port.state->port.tty, + up->uart_dma.rx_buf + + (up->uart_dma.prev_rx_dma_pos - + up->uart_dma.rx_buf_dma_phys), + curr_transmitted_size); + tty_flip_buffer_push(up->port.state->port.tty); + up->uart_dma.prev_rx_dma_pos = curr_dma_pos; + if (up->uart_dma.rx_buf_size + + up->uart_dma.rx_buf_dma_phys == curr_dma_pos) { + ret = serial_omap_start_rxdma(up); + if (ret < 0) { + serial_omap_stop_rxdma(up); + up->ier |= (UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + } + } else { + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + } + up->port_activity = jiffies; +} + +static void uart_rx_dma_callback(int lch, u16 ch_status, void *data) +{ + return; +} + +static int serial_omap_start_rxdma(struct uart_omap_port *up) +{ + int ret = 0; + + if (up->uart_dma.rx_dma_channel == -1) { + ret = omap_request_dma(up->uart_dma.uart_dma_rx, + "UART Rx DMA", + (void *)uart_rx_dma_callback, up, + &(up->uart_dma.rx_dma_channel)); + if (ret < 0) + return ret; + + omap_set_dma_src_params(up->uart_dma.rx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_dest_params(up->uart_dma.rx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, + up->uart_dma.rx_buf_dma_phys, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.rx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.rx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_rx, 0); + } + up->uart_dma.prev_rx_dma_pos = up->uart_dma.rx_buf_dma_phys; + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.rx_dma_channel); + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + up->uart_dma.rx_dma_used = true; + return ret; +} + +static void serial_omap_continue_tx(struct uart_omap_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + unsigned int start = up->uart_dma.tx_buf_dma_phys + + (xmit->tail & (UART_XMIT_SIZE - 1)); + + if (uart_circ_empty(xmit)) + return; + + up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); + /* + * It is a circular buffer. See if the buffer has wounded back. + * If yes it will have to be transferred in two separate dma + * transfers + */ + if (start + up->uart_dma.tx_buf_size >= + up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) + up->uart_dma.tx_buf_size = + (up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - start; + omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, start, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.tx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_tx, 0); + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.tx_dma_channel); +} + +static void uart_tx_dma_callback(int lch, u16 ch_status, void *data) +{ + struct uart_omap_port *up = (struct uart_omap_port *)data; + struct circ_buf *xmit = &up->port.state->xmit; + + xmit->tail = (xmit->tail + up->uart_dma.tx_buf_size) & \ + (UART_XMIT_SIZE - 1); + up->port.icount.tx += up->uart_dma.tx_buf_size; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) { + spin_lock(&(up->uart_dma.tx_lock)); + serial_omap_stop_tx(&up->port); + up->uart_dma.tx_dma_used = false; + spin_unlock(&(up->uart_dma.tx_lock)); + } else { + omap_stop_dma(up->uart_dma.tx_dma_channel); + serial_omap_continue_tx(up); + } + up->port_activity = jiffies; + return; +} + +static int serial_omap_probe(struct platform_device *pdev) +{ + struct uart_omap_port *up; + struct resource *mem, *irq, *dma_tx, *dma_rx; + struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data; + int ret = -ENOSPC; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -ENODEV; + } + + if (!request_mem_region(mem->start, (mem->end - mem->start) + 1, + pdev->dev.driver->name)) { + dev_err(&pdev->dev, "memory region already claimed\n"); + return -EBUSY; + } + + dma_rx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); + if (!dma_rx) { + ret = -EINVAL; + goto err; + } + + dma_tx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); + if (!dma_tx) { + ret = -EINVAL; + goto err; + } + + up = kzalloc(sizeof(*up), GFP_KERNEL); + if (up == NULL) { + ret = -ENOMEM; + goto do_release_region; + } + sprintf(up->name, "OMAP UART%d", pdev->id); + up->pdev = pdev; + up->port.dev = &pdev->dev; + up->port.type = PORT_OMAP; + up->port.iotype = UPIO_MEM; + up->port.irq = irq->start; + + up->port.regshift = 2; + up->port.fifosize = 64; + up->port.ops = &serial_omap_pops; + up->port.line = pdev->id; + + up->port.membase = omap_up_info->membase; + up->port.mapbase = omap_up_info->mapbase; + up->port.flags = omap_up_info->flags; + up->port.irqflags = omap_up_info->irqflags; + up->port.uartclk = omap_up_info->uartclk; + up->uart_dma.uart_base = mem->start; + + if (omap_up_info->dma_enabled) { + up->uart_dma.uart_dma_tx = dma_tx->start; + up->uart_dma.uart_dma_rx = dma_rx->start; + up->use_dma = 1; + up->uart_dma.rx_buf_size = 4096; + up->uart_dma.rx_timeout = 2; + spin_lock_init(&(up->uart_dma.tx_lock)); + spin_lock_init(&(up->uart_dma.rx_lock)); + up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; + up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; + } + + ui[pdev->id] = up; + serial_omap_add_console_port(up); + + ret = uart_add_one_port(&serial_omap_reg, &up->port); + if (ret != 0) + goto do_release_region; + + platform_set_drvdata(pdev, up); + return 0; +err: + dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", + pdev->id, __func__, ret); +do_release_region: + release_mem_region(mem->start, (mem->end - mem->start) + 1); + return ret; +} + +static int serial_omap_remove(struct platform_device *dev) +{ + struct uart_omap_port *up = platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + if (up) { + uart_remove_one_port(&serial_omap_reg, &up->port); + kfree(up); + } + return 0; +} + +static struct platform_driver serial_omap_driver = { + .probe = serial_omap_probe, + .remove = serial_omap_remove, + + .suspend = serial_omap_suspend, + .resume = serial_omap_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init serial_omap_init(void) +{ + int ret; + + ret = uart_register_driver(&serial_omap_reg); + if (ret != 0) + return ret; + ret = platform_driver_register(&serial_omap_driver); + if (ret != 0) + uart_unregister_driver(&serial_omap_reg); + return ret; +} + +static void __exit serial_omap_exit(void) +{ + platform_driver_unregister(&serial_omap_driver); + uart_unregister_driver(&serial_omap_reg); +} + +module_init(serial_omap_init); +module_exit(serial_omap_exit); + +MODULE_DESCRIPTION("OMAP High Speed UART driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments Inc"); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4b9eec68fad..78f9fd02c1b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -329,6 +329,13 @@ config SPI_STMP3XXX help SPI driver for Freescale STMP37xx/378x SoC SSP interface +config SPI_TEGRA + tristate "Nvidia Tegra SPI controller" + depends on ARCH_TEGRA + select TEGRA_SYSTEM_DMA + help + SPI driver for NVidia Tegra SoCs + config SPI_TOPCLIFF_PCH tristate "Topcliff PCH SPI Controller" depends on PCI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 557aaadf56b..8bc1a5abac1 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o +obj-$(CONFIG_SPI_TEGRA) += spi_tegra.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o diff --git a/drivers/spi/spi_tegra.c b/drivers/spi/spi_tegra.c new file mode 100644 index 00000000000..bb7df02a547 --- /dev/null +++ b/drivers/spi/spi_tegra.c @@ -0,0 +1,618 @@ +/* + * Driver for Nvidia TEGRA spi controller. + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Erik Gilling <konkers@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#include <linux/spi/spi.h> + +#include <mach/dma.h> + +#define SLINK_COMMAND 0x000 +#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0) +#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5) +#define SLINK_BOTH_EN (1 << 10) +#define SLINK_CS_SW (1 << 11) +#define SLINK_CS_VALUE (1 << 12) +#define SLINK_CS_POLARITY (1 << 13) +#define SLINK_IDLE_SDA_DRIVE_LOW (0 << 16) +#define SLINK_IDLE_SDA_DRIVE_HIGH (1 << 16) +#define SLINK_IDLE_SDA_PULL_LOW (2 << 16) +#define SLINK_IDLE_SDA_PULL_HIGH (3 << 16) +#define SLINK_IDLE_SDA_MASK (3 << 16) +#define SLINK_CS_POLARITY1 (1 << 20) +#define SLINK_CK_SDA (1 << 21) +#define SLINK_CS_POLARITY2 (1 << 22) +#define SLINK_CS_POLARITY3 (1 << 23) +#define SLINK_IDLE_SCLK_DRIVE_LOW (0 << 24) +#define SLINK_IDLE_SCLK_DRIVE_HIGH (1 << 24) +#define SLINK_IDLE_SCLK_PULL_LOW (2 << 24) +#define SLINK_IDLE_SCLK_PULL_HIGH (3 << 24) +#define SLINK_IDLE_SCLK_MASK (3 << 24) +#define SLINK_M_S (1 << 28) +#define SLINK_WAIT (1 << 29) +#define SLINK_GO (1 << 30) +#define SLINK_ENB (1 << 31) + +#define SLINK_COMMAND2 0x004 +#define SLINK_LSBFE (1 << 0) +#define SLINK_SSOE (1 << 1) +#define SLINK_SPIE (1 << 4) +#define SLINK_BIDIROE (1 << 6) +#define SLINK_MODFEN (1 << 7) +#define SLINK_INT_SIZE(x) (((x) & 0x1f) << 8) +#define SLINK_CS_ACTIVE_BETWEEN (1 << 17) +#define SLINK_SS_EN_CS(x) (((x) & 0x3) << 18) +#define SLINK_SS_SETUP(x) (((x) & 0x3) << 20) +#define SLINK_FIFO_REFILLS_0 (0 << 22) +#define SLINK_FIFO_REFILLS_1 (1 << 22) +#define SLINK_FIFO_REFILLS_2 (2 << 22) +#define SLINK_FIFO_REFILLS_3 (3 << 22) +#define SLINK_FIFO_REFILLS_MASK (3 << 22) +#define SLINK_WAIT_PACK_INT(x) (((x) & 0x7) << 26) +#define SLINK_SPC0 (1 << 29) +#define SLINK_TXEN (1 << 30) +#define SLINK_RXEN (1 << 31) + +#define SLINK_STATUS 0x008 +#define SLINK_COUNT(val) (((val) >> 0) & 0x1f) +#define SLINK_WORD(val) (((val) >> 5) & 0x1f) +#define SLINK_BLK_CNT(val) (((val) >> 0) & 0xffff) +#define SLINK_MODF (1 << 16) +#define SLINK_RX_UNF (1 << 18) +#define SLINK_TX_OVF (1 << 19) +#define SLINK_TX_FULL (1 << 20) +#define SLINK_TX_EMPTY (1 << 21) +#define SLINK_RX_FULL (1 << 22) +#define SLINK_RX_EMPTY (1 << 23) +#define SLINK_TX_UNF (1 << 24) +#define SLINK_RX_OVF (1 << 25) +#define SLINK_TX_FLUSH (1 << 26) +#define SLINK_RX_FLUSH (1 << 27) +#define SLINK_SCLK (1 << 28) +#define SLINK_ERR (1 << 29) +#define SLINK_RDY (1 << 30) +#define SLINK_BSY (1 << 31) + +#define SLINK_MAS_DATA 0x010 +#define SLINK_SLAVE_DATA 0x014 + +#define SLINK_DMA_CTL 0x018 +#define SLINK_DMA_BLOCK_SIZE(x) (((x) & 0xffff) << 0) +#define SLINK_TX_TRIG_1 (0 << 16) +#define SLINK_TX_TRIG_4 (1 << 16) +#define SLINK_TX_TRIG_8 (2 << 16) +#define SLINK_TX_TRIG_16 (3 << 16) +#define SLINK_TX_TRIG_MASK (3 << 16) +#define SLINK_RX_TRIG_1 (0 << 18) +#define SLINK_RX_TRIG_4 (1 << 18) +#define SLINK_RX_TRIG_8 (2 << 18) +#define SLINK_RX_TRIG_16 (3 << 18) +#define SLINK_RX_TRIG_MASK (3 << 18) +#define SLINK_PACKED (1 << 20) +#define SLINK_PACK_SIZE_4 (0 << 21) +#define SLINK_PACK_SIZE_8 (1 << 21) +#define SLINK_PACK_SIZE_16 (2 << 21) +#define SLINK_PACK_SIZE_32 (3 << 21) +#define SLINK_PACK_SIZE_MASK (3 << 21) +#define SLINK_IE_TXC (1 << 26) +#define SLINK_IE_RXC (1 << 27) +#define SLINK_DMA_EN (1 << 31) + +#define SLINK_STATUS2 0x01c +#define SLINK_TX_FIFO_EMPTY_COUNT(val) (((val) & 0x3f) >> 0) +#define SLINK_RX_FIFO_FULL_COUNT(val) (((val) & 0x3f) >> 16) + +#define SLINK_TX_FIFO 0x100 +#define SLINK_RX_FIFO 0x180 + +static const unsigned long spi_tegra_req_sels[] = { + TEGRA_DMA_REQ_SEL_SL2B1, + TEGRA_DMA_REQ_SEL_SL2B2, + TEGRA_DMA_REQ_SEL_SL2B3, + TEGRA_DMA_REQ_SEL_SL2B4, +}; + +#define BB_LEN 32 + +struct spi_tegra_data { + struct spi_master *master; + struct platform_device *pdev; + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + unsigned long phys; + + u32 cur_speed; + + struct list_head queue; + struct spi_transfer *cur; + unsigned cur_pos; + unsigned cur_len; + unsigned cur_bytes_per_word; + + /* The tegra spi controller has a bug which causes the first word + * in PIO transactions to be garbage. Since packed DMA transactions + * require transfers to be 4 byte aligned we need a bounce buffer + * for the generic case. + */ + struct tegra_dma_req rx_dma_req; + struct tegra_dma_channel *rx_dma; + u32 *rx_bb; + dma_addr_t rx_bb_phys; +}; + + +static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi, + unsigned long reg) +{ + return readl(tspi->base + reg); +} + +static inline void spi_tegra_writel(struct spi_tegra_data *tspi, + unsigned long val, + unsigned long reg) +{ + writel(val, tspi->base + reg); +} + +static void spi_tegra_go(struct spi_tegra_data *tspi) +{ + unsigned long val; + + wmb(); + + val = spi_tegra_readl(tspi, SLINK_DMA_CTL); + val &= ~SLINK_DMA_BLOCK_SIZE(~0) & ~SLINK_DMA_EN; + val |= SLINK_DMA_BLOCK_SIZE(tspi->rx_dma_req.size / 4 - 1); + spi_tegra_writel(tspi, val, SLINK_DMA_CTL); + + tegra_dma_enqueue_req(tspi->rx_dma, &tspi->rx_dma_req); + + val |= SLINK_DMA_EN; + spi_tegra_writel(tspi, val, SLINK_DMA_CTL); +} + +static unsigned spi_tegra_fill_tx_fifo(struct spi_tegra_data *tspi, + struct spi_transfer *t) +{ + unsigned len = min(t->len - tspi->cur_pos, BB_LEN * + tspi->cur_bytes_per_word); + u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_pos; + int i, j; + unsigned long val; + + val = spi_tegra_readl(tspi, SLINK_COMMAND); + val &= ~SLINK_WORD_SIZE(~0); + val |= SLINK_WORD_SIZE(len / tspi->cur_bytes_per_word - 1); + spi_tegra_writel(tspi, val, SLINK_COMMAND); + + for (i = 0; i < len; i += tspi->cur_bytes_per_word) { + val = 0; + for (j = 0; j < tspi->cur_bytes_per_word; j++) + val |= tx_buf[i + j] << j * 8; + + spi_tegra_writel(tspi, val, SLINK_TX_FIFO); + } + + tspi->rx_dma_req.size = len / tspi->cur_bytes_per_word * 4; + + return len; +} + +static unsigned spi_tegra_drain_rx_fifo(struct spi_tegra_data *tspi, + struct spi_transfer *t) +{ + unsigned len = tspi->cur_len; + u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_pos; + int i, j; + unsigned long val; + + for (i = 0; i < len; i += tspi->cur_bytes_per_word) { + val = tspi->rx_bb[i / tspi->cur_bytes_per_word]; + for (j = 0; j < tspi->cur_bytes_per_word; j++) + rx_buf[i + j] = (val >> (j * 8)) & 0xff; + } + + return len; +} + +static void spi_tegra_start_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); + u32 speed; + u8 bits_per_word; + unsigned long val; + + speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; + bits_per_word = t->bits_per_word ? t->bits_per_word : + spi->bits_per_word; + + tspi->cur_bytes_per_word = (bits_per_word - 1) / 8 + 1; + + if (speed != tspi->cur_speed) + clk_set_rate(tspi->clk, speed); + + if (tspi->cur_speed == 0) + clk_enable(tspi->clk); + + tspi->cur_speed = speed; + + val = spi_tegra_readl(tspi, SLINK_COMMAND2); + val &= ~SLINK_SS_EN_CS(~0) | SLINK_RXEN | SLINK_TXEN; + if (t->rx_buf) + val |= SLINK_RXEN; + if (t->tx_buf) + val |= SLINK_TXEN; + val |= SLINK_SS_EN_CS(spi->chip_select); + val |= SLINK_SPIE; + spi_tegra_writel(tspi, val, SLINK_COMMAND2); + + val = spi_tegra_readl(tspi, SLINK_COMMAND); + val &= ~SLINK_BIT_LENGTH(~0); + val |= SLINK_BIT_LENGTH(bits_per_word - 1); + + /* FIXME: should probably control CS manually so that we can be sure + * it does not go low between transfer and to support delay_usecs + * correctly. + */ + val &= ~SLINK_IDLE_SCLK_MASK & ~SLINK_CK_SDA & ~SLINK_CS_SW; + + if (spi->mode & SPI_CPHA) + val |= SLINK_CK_SDA; + + if (spi->mode & SPI_CPOL) + val |= SLINK_IDLE_SCLK_DRIVE_HIGH; + else + val |= SLINK_IDLE_SCLK_DRIVE_LOW; + + val |= SLINK_M_S; + + spi_tegra_writel(tspi, val, SLINK_COMMAND); + + spi_tegra_writel(tspi, SLINK_RX_FLUSH | SLINK_TX_FLUSH, SLINK_STATUS); + + tspi->cur = t; + tspi->cur_pos = 0; + tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, t); + + spi_tegra_go(tspi); +} + +static void spi_tegra_start_message(struct spi_device *spi, + struct spi_message *m) +{ + struct spi_transfer *t; + + m->actual_length = 0; + m->status = 0; + + t = list_first_entry(&m->transfers, struct spi_transfer, transfer_list); + spi_tegra_start_transfer(spi, t); +} + +static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req) +{ + struct spi_tegra_data *tspi = req->dev; + unsigned long flags; + struct spi_message *m; + struct spi_device *spi; + int timeout = 0; + unsigned long val; + + /* the SPI controller may come back with both the BSY and RDY bits + * set. In this case we need to wait for the BSY bit to clear so + * that we are sure the DMA is finished. 1000 reads was empirically + * determined to be long enough. + */ + while (timeout++ < 1000) { + if (!(spi_tegra_readl(tspi, SLINK_STATUS) & SLINK_BSY)) + break; + } + + spin_lock_irqsave(&tspi->lock, flags); + + val = spi_tegra_readl(tspi, SLINK_STATUS); + val |= SLINK_RDY; + spi_tegra_writel(tspi, val, SLINK_STATUS); + + m = list_first_entry(&tspi->queue, struct spi_message, queue); + + if (timeout >= 1000) + m->status = -EIO; + + spi = m->state; + + tspi->cur_pos += spi_tegra_drain_rx_fifo(tspi, tspi->cur); + m->actual_length += tspi->cur_pos; + + if (tspi->cur_pos < tspi->cur->len) { + tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, tspi->cur); + spi_tegra_go(tspi); + } else if (!list_is_last(&tspi->cur->transfer_list, + &m->transfers)) { + tspi->cur = list_first_entry(&tspi->cur->transfer_list, + struct spi_transfer, + transfer_list); + spi_tegra_start_transfer(spi, tspi->cur); + } else { + list_del(&m->queue); + + m->complete(m->context); + + if (!list_empty(&tspi->queue)) { + m = list_first_entry(&tspi->queue, struct spi_message, + queue); + spi = m->state; + spi_tegra_start_message(spi, m); + } else { + clk_disable(tspi->clk); + tspi->cur_speed = 0; + } + } + + spin_unlock_irqrestore(&tspi->lock, flags); +} + +static int spi_tegra_setup(struct spi_device *spi) +{ + struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); + unsigned long cs_bit; + unsigned long val; + unsigned long flags; + + dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n", + spi->bits_per_word, + spi->mode & SPI_CPOL ? "" : "~", + spi->mode & SPI_CPHA ? "" : "~", + spi->max_speed_hz); + + + switch (spi->chip_select) { + case 0: + cs_bit = SLINK_CS_POLARITY; + break; + + case 1: + cs_bit = SLINK_CS_POLARITY1; + break; + + case 2: + cs_bit = SLINK_CS_POLARITY2; + break; + + case 4: + cs_bit = SLINK_CS_POLARITY3; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&tspi->lock, flags); + + val = spi_tegra_readl(tspi, SLINK_COMMAND); + if (spi->mode & SPI_CS_HIGH) + val |= cs_bit; + else + val &= ~cs_bit; + spi_tegra_writel(tspi, val, SLINK_COMMAND); + + spin_unlock_irqrestore(&tspi->lock, flags); + + return 0; +} + +static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); + struct spi_transfer *t; + unsigned long flags; + int was_empty; + + if (list_empty(&m->transfers) || !m->complete) + return -EINVAL; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->bits_per_word < 0 || t->bits_per_word > 32) + return -EINVAL; + + if (t->len == 0) + return -EINVAL; + + if (!t->rx_buf && !t->tx_buf) + return -EINVAL; + } + + m->state = spi; + + spin_lock_irqsave(&tspi->lock, flags); + was_empty = list_empty(&tspi->queue); + list_add_tail(&m->queue, &tspi->queue); + + if (was_empty) + spi_tegra_start_message(spi, m); + + spin_unlock_irqrestore(&tspi->lock, flags); + + return 0; +} + +static int __init spi_tegra_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct spi_tegra_data *tspi; + struct resource *r; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof *tspi); + if (master == NULL) { + dev_err(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + + master->bus_num = pdev->id; + + master->setup = spi_tegra_setup; + master->transfer = spi_tegra_transfer; + master->num_chipselect = 4; + + dev_set_drvdata(&pdev->dev, master); + tspi = spi_master_get_devdata(master); + tspi->master = master; + tspi->pdev = pdev; + spin_lock_init(&tspi->lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENODEV; + goto err0; + } + + if (!request_mem_region(r->start, (r->end - r->start) + 1, + dev_name(&pdev->dev))) { + ret = -EBUSY; + goto err0; + } + + tspi->phys = r->start; + tspi->base = ioremap(r->start, r->end - r->start + 1); + if (!tspi->base) { + dev_err(&pdev->dev, "can't ioremap iomem\n"); + ret = -ENOMEM; + goto err1; + } + + tspi->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR_OR_NULL(tspi->clk)) { + dev_err(&pdev->dev, "can not get clock\n"); + ret = PTR_ERR(tspi->clk); + goto err2; + } + + INIT_LIST_HEAD(&tspi->queue); + + tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); + if (!tspi->rx_dma) { + dev_err(&pdev->dev, "can not allocate rx dma channel\n"); + ret = -ENODEV; + goto err3; + } + + tspi->rx_bb = dma_alloc_coherent(&pdev->dev, sizeof(u32) * BB_LEN, + &tspi->rx_bb_phys, GFP_KERNEL); + if (!tspi->rx_bb) { + dev_err(&pdev->dev, "can not allocate rx bounce buffer\n"); + ret = -ENOMEM; + goto err4; + } + + tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete; + tspi->rx_dma_req.to_memory = 1; + tspi->rx_dma_req.dest_addr = tspi->rx_bb_phys; + tspi->rx_dma_req.dest_bus_width = 32; + tspi->rx_dma_req.source_addr = tspi->phys + SLINK_RX_FIFO; + tspi->rx_dma_req.source_bus_width = 32; + tspi->rx_dma_req.source_wrap = 4; + tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id]; + tspi->rx_dma_req.dev = tspi; + + ret = spi_register_master(master); + + if (ret < 0) + goto err5; + + return ret; + +err5: + dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN, + tspi->rx_bb, tspi->rx_bb_phys); +err4: + tegra_dma_free_channel(tspi->rx_dma); +err3: + clk_put(tspi->clk); +err2: + iounmap(tspi->base); +err1: + release_mem_region(r->start, (r->end - r->start) + 1); +err0: + spi_master_put(master); + return ret; +} + +static int __devexit spi_tegra_remove(struct platform_device *pdev) +{ + struct spi_master *master; + struct spi_tegra_data *tspi; + struct resource *r; + + master = dev_get_drvdata(&pdev->dev); + tspi = spi_master_get_devdata(master); + + tegra_dma_free_channel(tspi->rx_dma); + + dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN, + tspi->rx_bb, tspi->rx_bb_phys); + + clk_put(tspi->clk); + iounmap(tspi->base); + + spi_master_put(master); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(r->start, (r->end - r->start) + 1); + + return 0; +} + +MODULE_ALIAS("platform:spi_tegra"); + +static struct platform_driver spi_tegra_driver = { + .driver = { + .name = "spi_tegra", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(spi_tegra_remove), +}; + +static int __init spi_tegra_init(void) +{ + return platform_driver_probe(&spi_tegra_driver, spi_tegra_probe); +} +module_init(spi_tegra_init); + +static void __exit spi_tegra_exit(void) +{ + platform_driver_unregister(&spi_tegra_driver); +} +module_exit(spi_tegra_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 61d3ca6619b..cb5cd422f3f 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -54,7 +54,6 @@ #include <plat/dma.h> #include <plat/usb.h> -#include <plat/control.h> #include "omap_udc.h" @@ -2309,21 +2308,12 @@ static char *trx_mode(unsigned m, int enabled) static int proc_otg_show(struct seq_file *s) { u32 tmp; - u32 trans; - char *ctrl_name; + u32 trans = 0; + char *ctrl_name = "(UNKNOWN)"; + /* XXX This needs major revision for OMAP2+ */ tmp = omap_readl(OTG_REV); - if (cpu_is_omap24xx()) { - /* - * REVISIT: Not clear how this works on OMAP2. trans - * is ANDed to produce bits 7 and 8, which might make - * sense for USB_TRANSCEIVER_CTRL on OMAP1, - * but with CONTROL_DEVCONF, these bits have something to - * do with the frame adjustment counter and McBSP2. - */ - ctrl_name = "control_devconf"; - trans = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0); - } else { + if (cpu_class_is_omap1()) { ctrl_name = "tranceiver_ctrl"; trans = omap_readw(USB_TRANSCEIVER_CTRL); } diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c index e77e8e4280f..4ea187d9376 100644 --- a/drivers/video/au1200fb.c +++ b/drivers/video/au1200fb.c @@ -1079,7 +1079,7 @@ static int au1200fb_fb_check_var(struct fb_var_screeninfo *var, * clock can only be obtain by dividing this value by an even integer. * Fallback to a slower pixel clock if necessary. */ pixclock = max((u32)(PICOS2KHZ(var->pixclock) * 1000), fbi->monspecs.dclkmin); - pixclock = min(pixclock, min(fbi->monspecs.dclkmax, (u32)AU1200_LCD_MAX_CLK/2)); + pixclock = min3(pixclock, fbi->monspecs.dclkmax, (u32)AU1200_LCD_MAX_CLK/2); if (AU1200_LCD_MAX_CLK % pixclock) { int diff = AU1200_LCD_MAX_CLK % pixclock; diff --git a/drivers/video/omap/lcd_omap3beagle.c b/drivers/video/omap/lcd_omap3beagle.c index ca75cc2a87a..d7c6c3e0afc 100644 --- a/drivers/video/omap/lcd_omap3beagle.c +++ b/drivers/video/omap/lcd_omap3beagle.c @@ -25,8 +25,6 @@ #include <linux/gpio.h> #include <linux/i2c/twl.h> -#include <plat/mux.h> -#include <plat/mux.h> #include <asm/mach-types.h> #include "omapfb.h" diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index 881c9f77c75..12327bbfdbb 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig @@ -40,7 +40,7 @@ config PANEL_TPO_TD043MTEA1 config PANEL_ACX565AKM tristate "ACX565AKM Panel" - depends on OMAP2_DSS_SDI + depends on OMAP2_DSS_SDI && SPI select BACKLIGHT_CLASS_DEVICE help This is the LCD panel used on Nokia N900 diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index 07fbb8a733b..e7731065320 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -587,6 +587,9 @@ static int acx_panel_power_on(struct omap_dss_device *dssdev) dev_dbg(&dssdev->dev, "%s\n", __func__); + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + mutex_lock(&md->mutex); r = omapdss_sdi_display_enable(dssdev); @@ -644,6 +647,9 @@ static void acx_panel_power_off(struct omap_dss_device *dssdev) dev_dbg(&dssdev->dev, "%s\n", __func__); + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + mutex_lock(&md->mutex); if (!md->enabled) { diff --git a/drivers/video/omap2/displays/panel-generic.c b/drivers/video/omap2/displays/panel-generic.c index 300eff5de1b..395a68de399 100644 --- a/drivers/video/omap2/displays/panel-generic.c +++ b/drivers/video/omap2/displays/panel-generic.c @@ -39,6 +39,9 @@ static int generic_panel_power_on(struct omap_dss_device *dssdev) { int r; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + r = omapdss_dpi_display_enable(dssdev); if (r) goto err0; @@ -58,6 +61,9 @@ err0: static void generic_panel_power_off(struct omap_dss_device *dssdev) { + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + if (dssdev->platform_disable) dssdev->platform_disable(dssdev); diff --git a/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c b/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c index 10267461991..0c6896cea2d 100644 --- a/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c +++ b/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c @@ -43,6 +43,9 @@ static int sharp_lq_panel_power_on(struct omap_dss_device *dssdev) { int r; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + r = omapdss_dpi_display_enable(dssdev); if (r) goto err0; @@ -65,6 +68,9 @@ err0: static void sharp_lq_panel_power_off(struct omap_dss_device *dssdev) { + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + if (dssdev->platform_disable) dssdev->platform_disable(dssdev); diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c index 7d9eb2b1f5a..9a138f650e0 100644 --- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -135,6 +135,9 @@ static int sharp_ls_power_on(struct omap_dss_device *dssdev) { int r = 0; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + r = omapdss_dpi_display_enable(dssdev); if (r) goto err0; @@ -157,6 +160,9 @@ err0: static void sharp_ls_power_off(struct omap_dss_device *dssdev) { + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + if (dssdev->platform_disable) dssdev->platform_disable(dssdev); diff --git a/drivers/video/omap2/displays/panel-toppoly-tdo35s.c b/drivers/video/omap2/displays/panel-toppoly-tdo35s.c index e320e67d06f..526e906c8a6 100644 --- a/drivers/video/omap2/displays/panel-toppoly-tdo35s.c +++ b/drivers/video/omap2/displays/panel-toppoly-tdo35s.c @@ -46,6 +46,9 @@ static int toppoly_tdo_panel_power_on(struct omap_dss_device *dssdev) { int r; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + r = omapdss_dpi_display_enable(dssdev); if (r) goto err0; @@ -65,6 +68,9 @@ err0: static void toppoly_tdo_panel_power_off(struct omap_dss_device *dssdev) { + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + if (dssdev->platform_disable) dssdev->platform_disable(dssdev); diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index e866e76b13d..dbe9d43b485 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -269,6 +269,9 @@ static int tpo_td043_power_on(struct omap_dss_device *dssdev) int nreset_gpio = dssdev->reset_gpio; int r; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + r = omapdss_dpi_display_enable(dssdev); if (r) goto err0; @@ -308,6 +311,9 @@ static void tpo_td043_power_off(struct omap_dss_device *dssdev) struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); int nreset_gpio = dssdev->reset_gpio; + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + tpo_td043_write(tpo_td043->spi, 3, TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM); diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile index d71b5d9d71b..7db17b5e570 100644 --- a/drivers/video/omap2/dss/Makefile +++ b/drivers/video/omap2/dss/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_OMAP2_DSS) += omapdss.o -omapdss-y := core.o dss.o dispc.o display.o manager.o overlay.o +omapdss-y := core.o dss.o dss_features.o dispc.o display.o manager.o overlay.o omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index b3a498f22d3..8e89f604928 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -37,6 +37,7 @@ #include <plat/clock.h> #include "dss.h" +#include "dss_features.h" static struct { struct platform_device *pdev; @@ -502,6 +503,8 @@ static int omap_dss_probe(struct platform_device *pdev) core.pdev = pdev; + dss_features_init(); + dss_init_overlay_managers(pdev); dss_init_overlays(pdev); diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 5ecdc000409..fa40fa59a9a 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -39,6 +39,7 @@ #include <plat/display.h> #include "dss.h" +#include "dss_features.h" /* DISPC */ #define DISPC_BASE 0x48050400 @@ -139,6 +140,22 @@ struct omap_dispc_isr_data { u32 mask; }; +struct dispc_h_coef { + s8 hc4; + s8 hc3; + u8 hc2; + s8 hc1; + s8 hc0; +}; + +struct dispc_v_coef { + s8 vc22; + s8 vc2; + u8 vc1; + s8 vc0; + s8 vc00; +}; + #define REG_GET(idx, start, end) \ FLD_GET(dispc_read_reg(idx), start, end) @@ -564,106 +581,77 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, int vscaleup, int five_taps) { /* Coefficients for horizontal up-sampling */ - static const u32 coef_hup[8] = { - 0x00800000, - 0x0D7CF800, - 0x1E70F5FF, - 0x335FF5FE, - 0xF74949F7, - 0xF55F33FB, - 0xF5701EFE, - 0xF87C0DFF, + static const struct dispc_h_coef coef_hup[8] = { + { 0, 0, 128, 0, 0 }, + { -1, 13, 124, -8, 0 }, + { -2, 30, 112, -11, -1 }, + { -5, 51, 95, -11, -2 }, + { 0, -9, 73, 73, -9 }, + { -2, -11, 95, 51, -5 }, + { -1, -11, 112, 30, -2 }, + { 0, -8, 124, 13, -1 }, }; - /* Coefficients for horizontal down-sampling */ - static const u32 coef_hdown[8] = { - 0x24382400, - 0x28371FFE, - 0x2C361BFB, - 0x303516F9, - 0x11343311, - 0x1635300C, - 0x1B362C08, - 0x1F372804, + /* Coefficients for vertical up-sampling */ + static const struct dispc_v_coef coef_vup_3tap[8] = { + { 0, 0, 128, 0, 0 }, + { 0, 3, 123, 2, 0 }, + { 0, 12, 111, 5, 0 }, + { 0, 32, 89, 7, 0 }, + { 0, 0, 64, 64, 0 }, + { 0, 7, 89, 32, 0 }, + { 0, 5, 111, 12, 0 }, + { 0, 2, 123, 3, 0 }, }; - /* Coefficients for horizontal and vertical up-sampling */ - static const u32 coef_hvup[2][8] = { - { - 0x00800000, - 0x037B02FF, - 0x0C6F05FE, - 0x205907FB, - 0x00404000, - 0x075920FE, - 0x056F0CFF, - 0x027B0300, - }, - { - 0x00800000, - 0x0D7CF8FF, - 0x1E70F5FE, - 0x335FF5FB, - 0xF7404000, - 0xF55F33FE, - 0xF5701EFF, - 0xF87C0D00, - }, + static const struct dispc_v_coef coef_vup_5tap[8] = { + { 0, 0, 128, 0, 0 }, + { -1, 13, 124, -8, 0 }, + { -2, 30, 112, -11, -1 }, + { -5, 51, 95, -11, -2 }, + { 0, -9, 73, 73, -9 }, + { -2, -11, 95, 51, -5 }, + { -1, -11, 112, 30, -2 }, + { 0, -8, 124, 13, -1 }, }; - /* Coefficients for horizontal and vertical down-sampling */ - static const u32 coef_hvdown[2][8] = { - { - 0x24382400, - 0x28391F04, - 0x2D381B08, - 0x3237170C, - 0x123737F7, - 0x173732F9, - 0x1B382DFB, - 0x1F3928FE, - }, - { - 0x24382400, - 0x28371F04, - 0x2C361B08, - 0x3035160C, - 0x113433F7, - 0x163530F9, - 0x1B362CFB, - 0x1F3728FE, - }, + /* Coefficients for horizontal down-sampling */ + static const struct dispc_h_coef coef_hdown[8] = { + { 0, 36, 56, 36, 0 }, + { 4, 40, 55, 31, -2 }, + { 8, 44, 54, 27, -5 }, + { 12, 48, 53, 22, -7 }, + { -9, 17, 52, 51, 17 }, + { -7, 22, 53, 48, 12 }, + { -5, 27, 54, 44, 8 }, + { -2, 31, 55, 40, 4 }, }; - /* Coefficients for vertical up-sampling */ - static const u32 coef_vup[8] = { - 0x00000000, - 0x0000FF00, - 0x0000FEFF, - 0x0000FBFE, - 0x000000F7, - 0x0000FEFB, - 0x0000FFFE, - 0x000000FF, + /* Coefficients for vertical down-sampling */ + static const struct dispc_v_coef coef_vdown_3tap[8] = { + { 0, 36, 56, 36, 0 }, + { 0, 40, 57, 31, 0 }, + { 0, 45, 56, 27, 0 }, + { 0, 50, 55, 23, 0 }, + { 0, 18, 55, 55, 0 }, + { 0, 23, 55, 50, 0 }, + { 0, 27, 56, 45, 0 }, + { 0, 31, 57, 40, 0 }, }; - - /* Coefficients for vertical down-sampling */ - static const u32 coef_vdown[8] = { - 0x00000000, - 0x000004FE, - 0x000008FB, - 0x00000CF9, - 0x0000F711, - 0x0000F90C, - 0x0000FB08, - 0x0000FE04, + static const struct dispc_v_coef coef_vdown_5tap[8] = { + { 0, 36, 56, 36, 0 }, + { 4, 40, 55, 31, -2 }, + { 8, 44, 54, 27, -5 }, + { 12, 48, 53, 22, -7 }, + { -9, 17, 52, 51, 17 }, + { -7, 22, 53, 48, 12 }, + { -5, 27, 54, 44, 8 }, + { -2, 31, 55, 40, 4 }, }; - const u32 *h_coef; - const u32 *hv_coef; - const u32 *hv_coef_mod; - const u32 *v_coef; + const struct dispc_h_coef *h_coef; + const struct dispc_v_coef *v_coef; int i; if (hscaleup) @@ -671,47 +659,34 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, else h_coef = coef_hdown; - if (vscaleup) { - hv_coef = coef_hvup[five_taps]; - v_coef = coef_vup; - - if (hscaleup) - hv_coef_mod = NULL; - else - hv_coef_mod = coef_hvdown[five_taps]; - } else { - hv_coef = coef_hvdown[five_taps]; - v_coef = coef_vdown; - - if (hscaleup) - hv_coef_mod = coef_hvup[five_taps]; - else - hv_coef_mod = NULL; - } + if (vscaleup) + v_coef = five_taps ? coef_vup_5tap : coef_vup_3tap; + else + v_coef = five_taps ? coef_vdown_5tap : coef_vdown_3tap; for (i = 0; i < 8; i++) { u32 h, hv; - h = h_coef[i]; - - hv = hv_coef[i]; - - if (hv_coef_mod) { - hv &= 0xffffff00; - hv |= (hv_coef_mod[i] & 0xff); - } + h = FLD_VAL(h_coef[i].hc0, 7, 0) + | FLD_VAL(h_coef[i].hc1, 15, 8) + | FLD_VAL(h_coef[i].hc2, 23, 16) + | FLD_VAL(h_coef[i].hc3, 31, 24); + hv = FLD_VAL(h_coef[i].hc4, 7, 0) + | FLD_VAL(v_coef[i].vc0, 15, 8) + | FLD_VAL(v_coef[i].vc1, 23, 16) + | FLD_VAL(v_coef[i].vc2, 31, 24); _dispc_write_firh_reg(plane, i, h); _dispc_write_firhv_reg(plane, i, hv); } - if (!five_taps) - return; - - for (i = 0; i < 8; i++) { - u32 v; - v = v_coef[i]; - _dispc_write_firv_reg(plane, i, v); + if (five_taps) { + for (i = 0; i < 8; i++) { + u32 v; + v = FLD_VAL(v_coef[i].vc00, 7, 0) + | FLD_VAL(v_coef[i].vc22, 15, 8); + _dispc_write_firv_reg(plane, i, v); + } } } @@ -800,12 +775,12 @@ static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) { - - BUG_ON(plane == OMAP_DSS_VIDEO1); - - if (cpu_is_omap24xx()) + if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) return; + BUG_ON(!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && + plane == OMAP_DSS_VIDEO1); + if (plane == OMAP_DSS_GFX) REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0); else if (plane == OMAP_DSS_VIDEO2) @@ -975,17 +950,14 @@ static void dispc_read_plane_fifo_sizes(void) DISPC_VID_FIFO_SIZE_STATUS(1) }; u32 size; int plane; + u8 start, end; enable_clocks(1); - for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) { - if (cpu_is_omap24xx()) - size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0); - else if (cpu_is_omap34xx()) - size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0); - else - BUG(); + dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end); + for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) { + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), start, end); dispc.fifo_size[plane] = size; } @@ -1002,6 +974,8 @@ void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high) const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, DISPC_VID_FIFO_THRESHOLD(0), DISPC_VID_FIFO_THRESHOLD(1) }; + u8 hi_start, hi_end, lo_start, lo_end; + enable_clocks(1); DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", @@ -1010,12 +984,12 @@ void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high) REG_GET(ftrs_reg[plane], 27, 16), low, high); - if (cpu_is_omap24xx()) - dispc_write_reg(ftrs_reg[plane], - FLD_VAL(high, 24, 16) | FLD_VAL(low, 8, 0)); - else - dispc_write_reg(ftrs_reg[plane], - FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 0)); + dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end); + dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end); + + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, hi_start, hi_end) | + FLD_VAL(low, lo_start, lo_end)); enable_clocks(0); } @@ -1035,13 +1009,16 @@ static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc) u32 val; const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0), DISPC_VID_FIR(1) }; + u8 hinc_start, hinc_end, vinc_start, vinc_end; BUG_ON(plane == OMAP_DSS_GFX); - if (cpu_is_omap24xx()) - val = FLD_VAL(vinc, 27, 16) | FLD_VAL(hinc, 11, 0); - else - val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0); + dss_feat_get_reg_field(FEAT_REG_FIRHINC, &hinc_start, &hinc_end); + dss_feat_get_reg_field(FEAT_REG_FIRVINC, &vinc_start, &vinc_end); + + val = FLD_VAL(vinc, vinc_start, vinc_end) | + FLD_VAL(hinc, hinc_start, hinc_end); + dispc_write_reg(fir_reg[plane-1], val); } @@ -1567,6 +1544,8 @@ static int _dispc_setup_plane(enum omap_plane plane, case OMAP_DSS_COLOR_ARGB16: case OMAP_DSS_COLOR_ARGB32: case OMAP_DSS_COLOR_RGBA32: + if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) + return -EINVAL; case OMAP_DSS_COLOR_RGBX32: if (cpu_is_omap24xx()) return -EINVAL; @@ -1607,9 +1586,10 @@ static int _dispc_setup_plane(enum omap_plane plane, case OMAP_DSS_COLOR_ARGB16: case OMAP_DSS_COLOR_ARGB32: case OMAP_DSS_COLOR_RGBA32: - if (cpu_is_omap24xx()) + if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) return -EINVAL; - if (plane == OMAP_DSS_VIDEO1) + if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && + plane == OMAP_DSS_VIDEO1) return -EINVAL; break; @@ -2002,7 +1982,7 @@ void dispc_enable_trans_key(enum omap_channel ch, bool enable) } void dispc_enable_alpha_blending(enum omap_channel ch, bool enable) { - if (cpu_is_omap24xx()) + if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) return; enable_clocks(1); @@ -2016,7 +1996,7 @@ bool dispc_alpha_blending_enabled(enum omap_channel ch) { bool enabled; - if (cpu_is_omap24xx()) + if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) return false; enable_clocks(1); diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index b3fa3a7db91..aa4f7a5fae2 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -3274,7 +3274,6 @@ int dsi_init(struct platform_device *pdev) dsi.vdds_dsi_reg = dss_get_vdds_dsi(); if (IS_ERR(dsi.vdds_dsi_reg)) { - iounmap(dsi.base); DSSERR("can't get VDDS_DSI regulator\n"); r = PTR_ERR(dsi.vdds_dsi_reg); goto err2; diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c new file mode 100644 index 00000000000..867f68de125 --- /dev/null +++ b/drivers/video/omap2/dss/dss_features.c @@ -0,0 +1,191 @@ +/* + * linux/drivers/video/omap2/dss/dss_features.c + * + * Copyright (C) 2010 Texas Instruments + * Author: Archit Taneja <archit@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/slab.h> + +#include <plat/display.h> +#include <plat/cpu.h> + +#include "dss_features.h" + +/* Defines a generic omap register field */ +struct dss_reg_field { + enum dss_feat_reg_field id; + u8 start, end; +}; + +struct omap_dss_features { + const struct dss_reg_field *reg_fields; + const int num_reg_fields; + + const u32 has_feature; + + const int num_mgrs; + const int num_ovls; + const enum omap_display_type *supported_displays; + const enum omap_color_mode *supported_color_modes; +}; + +/* This struct is assigned to one of the below during initialization */ +static struct omap_dss_features *omap_current_dss_features; + +static const struct dss_reg_field omap2_dss_reg_fields[] = { + { FEAT_REG_FIRHINC, 11, 0 }, + { FEAT_REG_FIRVINC, 27, 16 }, + { FEAT_REG_FIFOLOWTHRESHOLD, 8, 0 }, + { FEAT_REG_FIFOHIGHTHRESHOLD, 24, 16 }, + { FEAT_REG_FIFOSIZE, 8, 0 }, +}; + +static const struct dss_reg_field omap3_dss_reg_fields[] = { + { FEAT_REG_FIRHINC, 12, 0 }, + { FEAT_REG_FIRVINC, 28, 16 }, + { FEAT_REG_FIFOLOWTHRESHOLD, 11, 0 }, + { FEAT_REG_FIFOHIGHTHRESHOLD, 27, 16 }, + { FEAT_REG_FIFOSIZE, 10, 0 }, +}; + +static const enum omap_display_type omap2_dss_supported_displays[] = { + /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | + OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI, + + /* OMAP_DSS_CHANNEL_DIGIT */ + OMAP_DISPLAY_TYPE_VENC, +}; + +static const enum omap_display_type omap3_dss_supported_displays[] = { + /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | + OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI, + + /* OMAP_DSS_CHANNEL_DIGIT */ + OMAP_DISPLAY_TYPE_VENC, +}; + +static const enum omap_color_mode omap2_dss_supported_color_modes[] = { + /* OMAP_DSS_GFX */ + OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | + OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 | + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 | + OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 | + OMAP_DSS_COLOR_UYVY, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 | + OMAP_DSS_COLOR_UYVY, +}; + +static const enum omap_color_mode omap3_dss_supported_color_modes[] = { + /* OMAP_DSS_GFX */ + OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | + OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 | + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P | + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 | + OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 | + OMAP_DSS_COLOR_UYVY | OMAP_DSS_COLOR_ARGB32 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32, +}; + +/* OMAP2 DSS Features */ +static struct omap_dss_features omap2_dss_features = { + .reg_fields = omap2_dss_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields), + + .num_mgrs = 2, + .num_ovls = 3, + .supported_displays = omap2_dss_supported_displays, + .supported_color_modes = omap2_dss_supported_color_modes, +}; + +/* OMAP3 DSS Features */ +static struct omap_dss_features omap3_dss_features = { + .reg_fields = omap3_dss_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), + + .has_feature = FEAT_GLOBAL_ALPHA, + + .num_mgrs = 2, + .num_ovls = 3, + .supported_displays = omap3_dss_supported_displays, + .supported_color_modes = omap3_dss_supported_color_modes, +}; + +/* Functions returning values related to a DSS feature */ +int dss_feat_get_num_mgrs(void) +{ + return omap_current_dss_features->num_mgrs; +} + +int dss_feat_get_num_ovls(void) +{ + return omap_current_dss_features->num_ovls; +} + +enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel) +{ + return omap_current_dss_features->supported_displays[channel]; +} + +enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane) +{ + return omap_current_dss_features->supported_color_modes[plane]; +} + +/* DSS has_feature check */ +bool dss_has_feature(enum dss_feat_id id) +{ + return omap_current_dss_features->has_feature & id; +} + +void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end) +{ + if (id >= omap_current_dss_features->num_reg_fields) + BUG(); + + *start = omap_current_dss_features->reg_fields[id].start; + *end = omap_current_dss_features->reg_fields[id].end; +} + +void dss_features_init(void) +{ + if (cpu_is_omap24xx()) + omap_current_dss_features = &omap2_dss_features; + else + omap_current_dss_features = &omap3_dss_features; +} diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h new file mode 100644 index 00000000000..cb231eaa9b3 --- /dev/null +++ b/drivers/video/omap2/dss/dss_features.h @@ -0,0 +1,50 @@ +/* + * linux/drivers/video/omap2/dss/dss_features.h + * + * Copyright (C) 2010 Texas Instruments + * Author: Archit Taneja <archit@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAP2_DSS_FEATURES_H +#define __OMAP2_DSS_FEATURES_H + +#define MAX_DSS_MANAGERS 2 +#define MAX_DSS_OVERLAYS 3 + +/* DSS has feature id */ +enum dss_feat_id { + FEAT_GLOBAL_ALPHA = 1 << 0, + FEAT_GLOBAL_ALPHA_VID1 = 1 << 1, +}; + +/* DSS register field id */ +enum dss_feat_reg_field { + FEAT_REG_FIRHINC, + FEAT_REG_FIRVINC, + FEAT_REG_FIFOHIGHTHRESHOLD, + FEAT_REG_FIFOLOWTHRESHOLD, + FEAT_REG_FIFOSIZE, +}; + +/* DSS Feature Functions */ +int dss_feat_get_num_mgrs(void); +int dss_feat_get_num_ovls(void); +enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel); +enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane); + +bool dss_has_feature(enum dss_feat_id id); +void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end); +void dss_features_init(void); +#endif diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index 6a649ab5539..545e9b9a4d9 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -33,6 +33,7 @@ #include <plat/cpu.h> #include "dss.h" +#include "dss_features.h" static int num_managers; static struct list_head manager_list; @@ -448,8 +449,8 @@ struct manager_cache_data { static struct { spinlock_t lock; - struct overlay_cache_data overlay_cache[3]; - struct manager_cache_data manager_cache[2]; + struct overlay_cache_data overlay_cache[MAX_DSS_OVERLAYS]; + struct manager_cache_data manager_cache[MAX_DSS_MANAGERS]; bool irq_enabled; } dss_cache; @@ -882,12 +883,12 @@ static int configure_dispc(void) { struct overlay_cache_data *oc; struct manager_cache_data *mc; - const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); - const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + const int num_ovls = dss_feat_get_num_ovls(); + const int num_mgrs = dss_feat_get_num_mgrs(); int i; int r; - bool mgr_busy[2]; - bool mgr_go[2]; + bool mgr_busy[MAX_DSS_MANAGERS]; + bool mgr_go[MAX_DSS_MANAGERS]; bool busy; r = 0; @@ -989,7 +990,7 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, { struct overlay_cache_data *oc; struct manager_cache_data *mc; - const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + const int num_ovls = dss_feat_get_num_ovls(); struct omap_overlay_manager *mgr; int i; u16 x, y, w, h; @@ -1121,8 +1122,8 @@ void dss_start_update(struct omap_dss_device *dssdev) { struct manager_cache_data *mc; struct overlay_cache_data *oc; - const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); - const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + const int num_ovls = dss_feat_get_num_ovls(); + const int num_mgrs = dss_feat_get_num_mgrs(); struct omap_overlay_manager *mgr; int i; @@ -1151,10 +1152,10 @@ static void dss_apply_irq_handler(void *data, u32 mask) { struct manager_cache_data *mc; struct overlay_cache_data *oc; - const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); - const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + const int num_ovls = dss_feat_get_num_ovls(); + const int num_mgrs = dss_feat_get_num_mgrs(); int i, r; - bool mgr_busy[2]; + bool mgr_busy[MAX_DSS_MANAGERS]; mgr_busy[0] = dispc_go_busy(0); mgr_busy[1] = dispc_go_busy(1); @@ -1461,7 +1462,7 @@ int dss_init_overlay_managers(struct platform_device *pdev) num_managers = 0; - for (i = 0; i < 2; ++i) { + for (i = 0; i < dss_feat_get_num_mgrs(); ++i) { struct omap_overlay_manager *mgr; mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); @@ -1471,14 +1472,10 @@ int dss_init_overlay_managers(struct platform_device *pdev) case 0: mgr->name = "lcd"; mgr->id = OMAP_DSS_CHANNEL_LCD; - mgr->supported_displays = - OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | - OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI; break; case 1: mgr->name = "tv"; mgr->id = OMAP_DSS_CHANNEL_DIGIT; - mgr->supported_displays = OMAP_DISPLAY_TYPE_VENC; break; } @@ -1494,6 +1491,8 @@ int dss_init_overlay_managers(struct platform_device *pdev) mgr->disable = &dss_mgr_disable; mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC; + mgr->supported_displays = + dss_feat_get_supported_displays(mgr->id); dss_overlay_setup_dispc_manager(mgr); diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index 244dca81a39..75642c22cac 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -35,6 +35,7 @@ #include <plat/cpu.h> #include "dss.h" +#include "dss_features.h" static int num_overlays; static struct list_head overlay_list; @@ -237,7 +238,8 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, /* Video1 plane does not support global alpha * to always make it 255 completely opaque */ - if (ovl->id == OMAP_DSS_VIDEO1) + if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && + ovl->id == OMAP_DSS_VIDEO1) info.global_alpha = 255; else info.global_alpha = simple_strtoul(buf, NULL, 10); @@ -510,11 +512,11 @@ static void omap_dss_add_overlay(struct omap_overlay *overlay) list_add_tail(&overlay->list, &overlay_list); } -static struct omap_overlay *dispc_overlays[3]; +static struct omap_overlay *dispc_overlays[MAX_DSS_OVERLAYS]; void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr) { - mgr->num_overlays = 3; + mgr->num_overlays = dss_feat_get_num_ovls(); mgr->overlays = dispc_overlays; } @@ -535,7 +537,7 @@ void dss_init_overlays(struct platform_device *pdev) num_overlays = 0; - for (i = 0; i < 3; ++i) { + for (i = 0; i < dss_feat_get_num_ovls(); ++i) { struct omap_overlay *ovl; ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); @@ -545,18 +547,12 @@ void dss_init_overlays(struct platform_device *pdev) case 0: ovl->name = "gfx"; ovl->id = OMAP_DSS_GFX; - ovl->supported_modes = cpu_is_omap34xx() ? - OMAP_DSS_COLOR_GFX_OMAP3 : - OMAP_DSS_COLOR_GFX_OMAP2; ovl->caps = OMAP_DSS_OVL_CAP_DISPC; ovl->info.global_alpha = 255; break; case 1: ovl->name = "vid1"; ovl->id = OMAP_DSS_VIDEO1; - ovl->supported_modes = cpu_is_omap34xx() ? - OMAP_DSS_COLOR_VID1_OMAP3 : - OMAP_DSS_COLOR_VID_OMAP2; ovl->caps = OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_DISPC; ovl->info.global_alpha = 255; @@ -564,9 +560,6 @@ void dss_init_overlays(struct platform_device *pdev) case 2: ovl->name = "vid2"; ovl->id = OMAP_DSS_VIDEO2; - ovl->supported_modes = cpu_is_omap34xx() ? - OMAP_DSS_COLOR_VID2_OMAP3 : - OMAP_DSS_COLOR_VID_OMAP2; ovl->caps = OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_DISPC; ovl->info.global_alpha = 255; @@ -579,6 +572,9 @@ void dss_init_overlays(struct platform_device *pdev) ovl->get_overlay_info = &dss_ovl_get_overlay_info; ovl->wait_for_go = &dss_ovl_wait_for_go; + ovl->supported_modes = + dss_feat_get_supported_color_modes(ovl->id); + omap_dss_add_overlay(ovl); r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, @@ -651,7 +647,7 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) } if (mgr) { - for (i = 0; i < 3; i++) { + for (i = 0; i < dss_feat_get_num_ovls(); i++) { struct omap_overlay *ovl; ovl = omap_dss_get_overlay(i); if (!ovl->manager || force) { diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig index 43496d6c377..65149b22cf3 100644 --- a/drivers/video/omap2/omapfb/Kconfig +++ b/drivers/video/omap2/omapfb/Kconfig @@ -3,7 +3,7 @@ menuconfig FB_OMAP2 depends on FB && OMAP2_DSS select OMAP2_VRAM - select OMAP2_VRFB + select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index 04034d410d6..6a704f176c2 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -714,10 +714,10 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) var->pixclock = timings.pixel_clock != 0 ? KHZ2PICOS(timings.pixel_clock) : 0; - var->left_margin = timings.hfp; - var->right_margin = timings.hbp; - var->upper_margin = timings.vfp; - var->lower_margin = timings.vbp; + var->left_margin = timings.hbp; + var->right_margin = timings.hfp; + var->upper_margin = timings.vbp; + var->lower_margin = timings.vfp; var->hsync_len = timings.hsw; var->vsync_len = timings.vsw; } else { @@ -2059,10 +2059,10 @@ static int omapfb_mode_to_timings(const char *mode_str, if (r != 0) { timings->pixel_clock = PICOS2KHZ(var.pixclock); - timings->hfp = var.left_margin; - timings->hbp = var.right_margin; - timings->vfp = var.upper_margin; - timings->vbp = var.lower_margin; + timings->hbp = var.left_margin; + timings->hfp = var.right_margin; + timings->vbp = var.upper_margin; + timings->vfp = var.lower_margin; timings->hsw = var.hsync_len; timings->vsw = var.vsync_len; timings->x_res = var.xres; @@ -2198,6 +2198,16 @@ static int omapfb_probe(struct platform_device *pdev) goto err0; } + /* TODO : Replace cpu check with omap_has_vrfb once HAS_FEATURE + * available for OMAP2 and OMAP3 + */ + if (def_vrfb && !cpu_is_omap24xx() && !cpu_is_omap34xx()) { + def_vrfb = 0; + dev_warn(&pdev->dev, "VRFB is not supported on this hardware, " + "ignoring the module parameter vrfb=y\n"); + } + + mutex_init(&fbdev->mtx); fbdev->dev = &pdev->dev; diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index 0c9ce88e95e..68bd23476c6 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -32,10 +32,14 @@ #include <linux/dma-mapping.h> #include <linux/of_device.h> #include <linux/of_platform.h> +#include <linux/of_address.h> #include <linux/io.h> #include <linux/xilinxfb.h> #include <linux/slab.h> + +#ifdef CONFIG_PPC_DCR #include <asm/dcr.h> +#endif #define DRIVER_NAME "xilinxfb" @@ -123,10 +127,10 @@ struct xilinxfb_drvdata { registers */ void __iomem *regs; /* virt. address of the control registers */ - +#ifdef CONFIG_PPC_DCR dcr_host_t dcr_host; unsigned int dcr_len; - +#endif void *fb_virt; /* virt. address of the frame buffer */ dma_addr_t fb_phys; /* phys. address of the frame buffer */ int fb_alloced; /* Flag, was the fb memory alloced? */ @@ -152,9 +156,10 @@ static void xilinx_fb_out_be32(struct xilinxfb_drvdata *drvdata, u32 offset, { if (drvdata->flags & PLB_ACCESS_FLAG) out_be32(drvdata->regs + (offset << 2), val); +#ifdef CONFIG_PPC_DCR else dcr_write(drvdata->dcr_host, offset, val); - +#endif } static int @@ -383,8 +388,11 @@ static int xilinxfb_release(struct device *dev) if (drvdata->flags & PLB_ACCESS_FLAG) { iounmap(drvdata->regs); release_mem_region(drvdata->regs_phys, 8); - } else + } +#ifdef CONFIG_PPC_DCR + else dcr_unmap(drvdata->dcr_host, drvdata->dcr_len); +#endif kfree(drvdata); dev_set_drvdata(dev, NULL); @@ -404,7 +412,7 @@ xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match) u32 tft_access; struct xilinxfb_platform_data pdata; struct resource res; - int size, rc, start; + int size, rc; struct xilinxfb_drvdata *drvdata; /* Copy with the default pdata (not a ptr reference!) */ @@ -437,7 +445,10 @@ xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match) dev_err(&op->dev, "invalid address\n"); goto err; } - } else { + } +#ifdef CONFIG_PPC_DCR + else { + int start; res.start = 0; start = dcr_resource_start(op->dev.of_node, 0); drvdata->dcr_len = dcr_resource_len(op->dev.of_node, 0); @@ -447,6 +458,7 @@ xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match) goto err; } } +#endif prop = of_get_property(op->dev.of_node, "phys-size", &size); if ((prop) && (size >= sizeof(u32)*2)) { diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 81e3d610089..3dd4971160e 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -38,11 +38,11 @@ #include <linux/err.h> #include <linux/platform_device.h> #include <linux/moduleparam.h> -#include <linux/clk.h> #include <linux/bitops.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #include <mach/hardware.h> #include <plat/prcm.h> @@ -61,8 +61,6 @@ struct omap_wdt_dev { void __iomem *base; /* physical */ struct device *dev; int omap_wdt_users; - struct clk *ick; - struct clk *fck; struct resource *mem; struct miscdevice omap_wdt_miscdev; }; @@ -146,8 +144,7 @@ static int omap_wdt_open(struct inode *inode, struct file *file) if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users))) return -EBUSY; - clk_enable(wdev->ick); /* Enable the interface clock */ - clk_enable(wdev->fck); /* Enable the functional clock */ + pm_runtime_get_sync(wdev->dev); /* initialize prescaler */ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) @@ -177,8 +174,7 @@ static int omap_wdt_release(struct inode *inode, struct file *file) omap_wdt_disable(wdev); - clk_disable(wdev->ick); - clk_disable(wdev->fck); + pm_runtime_put_sync(wdev->dev); #else printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); #endif @@ -293,19 +289,7 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) wdev->omap_wdt_users = 0; wdev->mem = mem; - - wdev->ick = clk_get(&pdev->dev, "ick"); - if (IS_ERR(wdev->ick)) { - ret = PTR_ERR(wdev->ick); - wdev->ick = NULL; - goto err_clk; - } - wdev->fck = clk_get(&pdev->dev, "fck"); - if (IS_ERR(wdev->fck)) { - ret = PTR_ERR(wdev->fck); - wdev->fck = NULL; - goto err_clk; - } + wdev->dev = &pdev->dev; wdev->base = ioremap(res->start, resource_size(res)); if (!wdev->base) { @@ -315,8 +299,8 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, wdev); - clk_enable(wdev->ick); - clk_enable(wdev->fck); + pm_runtime_enable(wdev->dev); + pm_runtime_get_sync(wdev->dev); omap_wdt_disable(wdev); omap_wdt_adjust_timeout(timer_margin); @@ -334,11 +318,7 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, timer_margin); - /* autogate OCP interface clock */ - __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG); - - clk_disable(wdev->ick); - clk_disable(wdev->fck); + pm_runtime_put_sync(wdev->dev); omap_wdt_dev = pdev; @@ -350,12 +330,6 @@ err_misc: err_ioremap: wdev->base = NULL; - -err_clk: - if (wdev->ick) - clk_put(wdev->ick); - if (wdev->fck) - clk_put(wdev->fck); kfree(wdev); err_kzalloc: @@ -387,8 +361,6 @@ static int __devexit omap_wdt_remove(struct platform_device *pdev) release_mem_region(res->start, resource_size(res)); platform_set_drvdata(pdev, NULL); - clk_put(wdev->ick); - clk_put(wdev->fck); iounmap(wdev->base); kfree(wdev); |