diff options
Diffstat (limited to 'drivers')
149 files changed, 7736 insertions, 3227 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 139f41f033d..251344cb29a 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -2,19 +2,18 @@ # ACPI Configuration # -menu "ACPI (Advanced Configuration and Power Interface) Support" +menuconfig ACPI + bool "ACPI Support (Advanced Configuration and Power Interface) Support" depends on !X86_NUMAQ depends on !X86_VISWS depends on !IA64_HP_SIM depends on IA64 || X86 - depends on PM - -config ACPI - bool "ACPI Support" - depends on IA64 || X86 depends on PCI depends on PM select PNP + # for sleep + select HOTPLUG_CPU if X86 && SMP + select SUSPEND_SMP if X86 && SMP default y ---help--- Advanced Configuration and Power Interface (ACPI) support for @@ -46,53 +45,26 @@ config ACPI if ACPI -config ACPI_SLEEP - bool "Sleep States" - depends on X86 && (!SMP || SUSPEND_SMP) - depends on PM - default y +config ACPI_PROCFS + bool "Deprecated /proc/acpi files" + depends on PROC_FS ---help--- - This option adds support for ACPI suspend states. - - With this option, you will be able to put the system "to sleep". - Sleep states are low power states for the system and devices. All - of the system operating state is saved to either memory or disk - (depending on the state), to allow the system to resume operation - quickly at your request. - - Although this option sounds really nifty, barely any of the device - drivers have been converted to the new driver model and hence few - have proper power management support. + For backwards compatibility, this option allows + depricated /proc/acpi/ files to exist, even when + they have been replaced by functions in /sys. + The deprecated files (and their replacements) include: - This option is not recommended for anyone except those doing driver - power management development. - -config ACPI_SLEEP_PROC_FS - bool - depends on ACPI_SLEEP && PROC_FS - default y + /proc/acpi/sleep (/sys/power/state) + /proc/acpi/info (/sys/modules/acpi/parameters/acpica_version) + /proc/acpi/dsdt (/sys/firmware/acpi/tables/DSDT) + /proc/acpi/fadt (/sys/firmware/acpi/tables/FACP) + /proc/acpi/debug_layer (/sys/module/acpi/parameters/debug_layer) + /proc/acpi/debug_level (/sys/module/acpi/parameters/debug_level) -config ACPI_SLEEP_PROC_SLEEP - bool "/proc/acpi/sleep (deprecated)" - depends on ACPI_SLEEP_PROC_FS - default n - ---help--- - Create /proc/acpi/sleep - Deprecated by /sys/power/state + This option has no effect on /proc/acpi/ files + and functions which do not yet exist in /sys. -config ACPI_PROCFS - bool "Procfs interface (deprecated)" - depends on ACPI - default y - ---help--- - The Procfs interface for ACPI is made optional for backward compatibility. - As the same functions are duplicated in the sysfs interface - and this proc interface will be removed some time later, - it's marked as deprecated. - ( /proc/acpi/debug_layer && debug_level are deprecated by - /sys/module/acpi/parameters/debug_layer && debug_level. - /proc/acpi/info is deprecated by - /sys/module/acpi/parameters/acpica_version ) + Say N to delete /proc/acpi/ files that have moved to /sys/ config ACPI_AC tristate "AC Adapter" @@ -124,7 +96,7 @@ config ACPI_BUTTON config ACPI_VIDEO tristate "Video" - depends on X86 && BACKLIGHT_CLASS_DEVICE + depends on X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL help This driver implement the ACPI Extensions For Display Adapters for integrated graphics devices on motherboard, as specified in @@ -280,6 +252,14 @@ config ACPI_DEBUG of verbosity. Saying Y enables these statements. This will increase your kernel size by around 50K. +config ACPI_DEBUG_FUNC_TRACE + bool "Additionally enable ACPI function tracing" + default n + depends on ACPI_DEBUG + help + ACPI Debug Statements slow down ACPI processing. Function trace + is about half of the penalty and is rarely useful. + config ACPI_EC bool default y @@ -330,7 +310,6 @@ config ACPI_CONTAINER config ACPI_HOTPLUG_MEMORY tristate "Memory Hotplug" - depends on ACPI depends on MEMORY_HOTPLUG default n help @@ -359,5 +338,3 @@ config ACPI_SBS to today's ACPI "Control Method" battery. endif # ACPI - -endmenu diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index e64c76c8b72..cad932de383 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -43,21 +43,30 @@ #define ACPI_BATTERY_CLASS "battery" #define ACPI_BATTERY_HID "PNP0C0A" #define ACPI_BATTERY_DEVICE_NAME "Battery" -#define ACPI_BATTERY_FILE_INFO "info" -#define ACPI_BATTERY_FILE_STATUS "state" -#define ACPI_BATTERY_FILE_ALARM "alarm" #define ACPI_BATTERY_NOTIFY_STATUS 0x80 #define ACPI_BATTERY_NOTIFY_INFO 0x81 #define ACPI_BATTERY_UNITS_WATTS "mW" #define ACPI_BATTERY_UNITS_AMPS "mA" #define _COMPONENT ACPI_BATTERY_COMPONENT + +#define ACPI_BATTERY_UPDATE_TIME 0 + +#define ACPI_BATTERY_NONE_UPDATE 0 +#define ACPI_BATTERY_EASY_UPDATE 1 +#define ACPI_BATTERY_INIT_UPDATE 2 + ACPI_MODULE_NAME("battery"); MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI Battery Driver"); MODULE_LICENSE("GPL"); +static unsigned int update_time = ACPI_BATTERY_UPDATE_TIME; + +/* 0 - every time, > 0 - by update_time */ +module_param(update_time, uint, 0644); + extern struct proc_dir_entry *acpi_lock_battery_dir(void); extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); @@ -76,7 +85,7 @@ static struct acpi_driver acpi_battery_driver = { }, }; -struct acpi_battery_status { +struct acpi_battery_state { acpi_integer state; acpi_integer present_rate; acpi_integer remaining_capacity; @@ -99,33 +108,111 @@ struct acpi_battery_info { acpi_string oem_info; }; -struct acpi_battery_flags { - u8 present:1; /* Bay occupied? */ - u8 power_unit:1; /* 0=watts, 1=apms */ - u8 alarm:1; /* _BTP present? */ - u8 reserved:5; +enum acpi_battery_files{ + ACPI_BATTERY_INFO = 0, + ACPI_BATTERY_STATE, + ACPI_BATTERY_ALARM, + ACPI_BATTERY_NUMFILES, }; -struct acpi_battery_trips { - unsigned long warning; - unsigned long low; +struct acpi_battery_flags { + u8 battery_present_prev; + u8 alarm_present; + u8 init_update; + u8 update[ACPI_BATTERY_NUMFILES]; + u8 power_unit; }; struct acpi_battery { - struct acpi_device * device; + struct mutex mutex; + struct acpi_device *device; struct acpi_battery_flags flags; - struct acpi_battery_trips trips; + struct acpi_buffer bif_data; + struct acpi_buffer bst_data; unsigned long alarm; - struct acpi_battery_info *info; + unsigned long update_time[ACPI_BATTERY_NUMFILES]; }; +inline int acpi_battery_present(struct acpi_battery *battery) +{ + return battery->device->status.battery_present; +} +inline char *acpi_battery_power_units(struct acpi_battery *battery) +{ + if (battery->flags.power_unit) + return ACPI_BATTERY_UNITS_AMPS; + else + return ACPI_BATTERY_UNITS_WATTS; +} + +inline acpi_handle acpi_battery_handle(struct acpi_battery *battery) +{ + return battery->device->handle; +} + /* -------------------------------------------------------------------------- Battery Management -------------------------------------------------------------------------- */ -static int -acpi_battery_get_info(struct acpi_battery *battery, - struct acpi_battery_info **bif) +static void acpi_battery_check_result(struct acpi_battery *battery, int result) +{ + if (!battery) + return; + + if (result) { + battery->flags.init_update = 1; + } +} + +static int acpi_battery_extract_package(struct acpi_battery *battery, + union acpi_object *package, + struct acpi_buffer *format, + struct acpi_buffer *data, + char *package_name) +{ + acpi_status status = AE_OK; + struct acpi_buffer data_null = { 0, NULL }; + + status = acpi_extract_package(package, format, &data_null); + if (status != AE_BUFFER_OVERFLOW) { + ACPI_EXCEPTION((AE_INFO, status, "Extracting size %s", + package_name)); + return -ENODEV; + } + + if (data_null.length != data->length) { + kfree(data->pointer); + data->pointer = kzalloc(data_null.length, GFP_KERNEL); + if (!data->pointer) { + ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY, "kzalloc()")); + return -ENOMEM; + } + data->length = data_null.length; + } + + status = acpi_extract_package(package, format, data); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Extracting %s", + package_name)); + return -ENODEV; + } + + return 0; +} + +static int acpi_battery_get_status(struct acpi_battery *battery) +{ + int result = 0; + + result = acpi_bus_get_status(battery->device); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA")); + return -ENODEV; + } + return result; +} + +static int acpi_battery_get_info(struct acpi_battery *battery) { int result = 0; acpi_status status = 0; @@ -133,16 +220,20 @@ acpi_battery_get_info(struct acpi_battery *battery, struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BIF), ACPI_BATTERY_FORMAT_BIF }; - struct acpi_buffer data = { 0, NULL }; union acpi_object *package = NULL; + struct acpi_buffer *data = NULL; + struct acpi_battery_info *bif = NULL; + battery->update_time[ACPI_BATTERY_INFO] = get_seconds(); - if (!battery || !bif) - return -EINVAL; + if (!acpi_battery_present(battery)) + return 0; - /* Evalute _BIF */ + /* Evaluate _BIF */ - status = acpi_evaluate_object(battery->device->handle, "_BIF", NULL, &buffer); + status = + acpi_evaluate_object(acpi_battery_handle(battery), "_BIF", NULL, + &buffer); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF")); return -ENODEV; @@ -150,41 +241,29 @@ acpi_battery_get_info(struct acpi_battery *battery, package = buffer.pointer; - /* Extract Package Data */ - - status = acpi_extract_package(package, &format, &data); - if (status != AE_BUFFER_OVERFLOW) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting _BIF")); - result = -ENODEV; - goto end; - } + data = &battery->bif_data; - data.pointer = kzalloc(data.length, GFP_KERNEL); - if (!data.pointer) { - result = -ENOMEM; - goto end; - } + /* Extract Package Data */ - status = acpi_extract_package(package, &format, &data); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting _BIF")); - kfree(data.pointer); - result = -ENODEV; + result = + acpi_battery_extract_package(battery, package, &format, data, + "_BIF"); + if (result) goto end; - } end: + kfree(buffer.pointer); - if (!result) - (*bif) = data.pointer; + if (!result) { + bif = data->pointer; + battery->flags.power_unit = bif->power_unit; + } return result; } -static int -acpi_battery_get_status(struct acpi_battery *battery, - struct acpi_battery_status **bst) +static int acpi_battery_get_state(struct acpi_battery *battery) { int result = 0; acpi_status status = 0; @@ -192,16 +271,19 @@ acpi_battery_get_status(struct acpi_battery *battery, struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BST), ACPI_BATTERY_FORMAT_BST }; - struct acpi_buffer data = { 0, NULL }; union acpi_object *package = NULL; + struct acpi_buffer *data = NULL; + battery->update_time[ACPI_BATTERY_STATE] = get_seconds(); - if (!battery || !bst) - return -EINVAL; + if (!acpi_battery_present(battery)) + return 0; - /* Evalute _BST */ + /* Evaluate _BST */ - status = acpi_evaluate_object(battery->device->handle, "_BST", NULL, &buffer); + status = + acpi_evaluate_object(acpi_battery_handle(battery), "_BST", NULL, + &buffer); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST")); return -ENODEV; @@ -209,55 +291,49 @@ acpi_battery_get_status(struct acpi_battery *battery, package = buffer.pointer; - /* Extract Package Data */ - - status = acpi_extract_package(package, &format, &data); - if (status != AE_BUFFER_OVERFLOW) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting _BST")); - result = -ENODEV; - goto end; - } + data = &battery->bst_data; - data.pointer = kzalloc(data.length, GFP_KERNEL); - if (!data.pointer) { - result = -ENOMEM; - goto end; - } + /* Extract Package Data */ - status = acpi_extract_package(package, &format, &data); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting _BST")); - kfree(data.pointer); - result = -ENODEV; + result = + acpi_battery_extract_package(battery, package, &format, data, + "_BST"); + if (result) goto end; - } end: kfree(buffer.pointer); - if (!result) - (*bst) = data.pointer; - return result; } -static int -acpi_battery_set_alarm(struct acpi_battery *battery, unsigned long alarm) +static int acpi_battery_get_alarm(struct acpi_battery *battery) +{ + battery->update_time[ACPI_BATTERY_ALARM] = get_seconds(); + + return 0; +} + +static int acpi_battery_set_alarm(struct acpi_battery *battery, + unsigned long alarm) { acpi_status status = 0; union acpi_object arg0 = { ACPI_TYPE_INTEGER }; struct acpi_object_list arg_list = { 1, &arg0 }; + battery->update_time[ACPI_BATTERY_ALARM] = get_seconds(); - if (!battery) - return -EINVAL; + if (!acpi_battery_present(battery)) + return -ENODEV; - if (!battery->flags.alarm) + if (!battery->flags.alarm_present) return -ENODEV; arg0.integer.value = alarm; - status = acpi_evaluate_object(battery->device->handle, "_BTP", &arg_list, NULL); + status = + acpi_evaluate_object(acpi_battery_handle(battery), "_BTP", + &arg_list, NULL); if (ACPI_FAILURE(status)) return -ENODEV; @@ -268,65 +344,114 @@ acpi_battery_set_alarm(struct acpi_battery *battery, unsigned long alarm) return 0; } -static int acpi_battery_check(struct acpi_battery *battery) +static int acpi_battery_init_alarm(struct acpi_battery *battery) { int result = 0; acpi_status status = AE_OK; acpi_handle handle = NULL; - struct acpi_device *device = NULL; - struct acpi_battery_info *bif = NULL; + struct acpi_battery_info *bif = battery->bif_data.pointer; + unsigned long alarm = battery->alarm; + /* See if alarms are supported, and if so, set default */ - if (!battery) - return -EINVAL; - - device = battery->device; + status = acpi_get_handle(acpi_battery_handle(battery), "_BTP", &handle); + if (ACPI_SUCCESS(status)) { + battery->flags.alarm_present = 1; + if (!alarm && bif) { + alarm = bif->design_capacity_warning; + } + result = acpi_battery_set_alarm(battery, alarm); + if (result) + goto end; + } else { + battery->flags.alarm_present = 0; + } - result = acpi_bus_get_status(device); - if (result) - return result; + end: - /* Insertion? */ + return result; +} - if (!battery->flags.present && device->status.battery_present) { +static int acpi_battery_init_update(struct acpi_battery *battery) +{ + int result = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery inserted\n")); + result = acpi_battery_get_status(battery); + if (result) + return result; - /* Evalute _BIF to get certain static information */ + battery->flags.battery_present_prev = acpi_battery_present(battery); - result = acpi_battery_get_info(battery, &bif); + if (acpi_battery_present(battery)) { + result = acpi_battery_get_info(battery); + if (result) + return result; + result = acpi_battery_get_state(battery); if (result) return result; - battery->flags.power_unit = bif->power_unit; - battery->trips.warning = bif->design_capacity_warning; - battery->trips.low = bif->design_capacity_low; - kfree(bif); + acpi_battery_init_alarm(battery); + } + + return result; +} - /* See if alarms are supported, and if so, set default */ +static int acpi_battery_update(struct acpi_battery *battery, + int update, int *update_result_ptr) +{ + int result = 0; + int update_result = ACPI_BATTERY_NONE_UPDATE; + + if (!acpi_battery_present(battery)) { + update = 1; + } - status = acpi_get_handle(battery->device->handle, "_BTP", &handle); - if (ACPI_SUCCESS(status)) { - battery->flags.alarm = 1; - acpi_battery_set_alarm(battery, battery->trips.warning); + if (battery->flags.init_update) { + result = acpi_battery_init_update(battery); + if (result) + goto end; + update_result = ACPI_BATTERY_INIT_UPDATE; + } else if (update) { + result = acpi_battery_get_status(battery); + if (result) + goto end; + if ((!battery->flags.battery_present_prev & acpi_battery_present(battery)) + || (battery->flags.battery_present_prev & !acpi_battery_present(battery))) { + result = acpi_battery_init_update(battery); + if (result) + goto end; + update_result = ACPI_BATTERY_INIT_UPDATE; + } else { + update_result = ACPI_BATTERY_EASY_UPDATE; } } - /* Removal? */ + end: - else if (battery->flags.present && !device->status.battery_present) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery removed\n")); - } + battery->flags.init_update = (result != 0); - battery->flags.present = device->status.battery_present; + *update_result_ptr = update_result; return result; } -static void acpi_battery_check_present(struct acpi_battery *battery) +static void acpi_battery_notify_update(struct acpi_battery *battery) { - if (!battery->flags.present) { - acpi_battery_check(battery); + acpi_battery_get_status(battery); + + if (battery->flags.init_update) { + return; + } + + if ((!battery->flags.battery_present_prev & + acpi_battery_present(battery)) || + (battery->flags.battery_present_prev & + !acpi_battery_present(battery))) { + battery->flags.init_update = 1; + } else { + battery->flags.update[ACPI_BATTERY_INFO] = 1; + battery->flags.update[ACPI_BATTERY_STATE] = 1; + battery->flags.update[ACPI_BATTERY_ALARM] = 1; } } @@ -335,37 +460,33 @@ static void acpi_battery_check_present(struct acpi_battery *battery) -------------------------------------------------------------------------- */ static struct proc_dir_entry *acpi_battery_dir; -static int acpi_battery_read_info(struct seq_file *seq, void *offset) + +static int acpi_battery_print_info(struct seq_file *seq, int result) { - int result = 0; struct acpi_battery *battery = seq->private; struct acpi_battery_info *bif = NULL; char *units = "?"; - - if (!battery) + if (result) goto end; - acpi_battery_check_present(battery); - - if (battery->flags.present) + if (acpi_battery_present(battery)) seq_printf(seq, "present: yes\n"); else { seq_printf(seq, "present: no\n"); goto end; } - /* Battery Info (_BIF) */ - - result = acpi_battery_get_info(battery, &bif); - if (result || !bif) { - seq_printf(seq, "ERROR: Unable to read battery information\n"); + bif = battery->bif_data.pointer; + if (!bif) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BIF buffer is NULL")); + result = -ENODEV; goto end; } - units = - bif-> - power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; + /* Battery Units */ + + units = acpi_battery_power_units(battery); if (bif->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) seq_printf(seq, "design capacity: unknown\n"); @@ -396,7 +517,6 @@ static int acpi_battery_read_info(struct seq_file *seq, void *offset) else seq_printf(seq, "design voltage: %d mV\n", (u32) bif->design_voltage); - seq_printf(seq, "design capacity warning: %d %sh\n", (u32) bif->design_capacity_warning, units); seq_printf(seq, "design capacity low: %d %sh\n", @@ -411,50 +531,40 @@ static int acpi_battery_read_info(struct seq_file *seq, void *offset) seq_printf(seq, "OEM info: %s\n", bif->oem_info); end: - kfree(bif); - return 0; -} + if (result) + seq_printf(seq, "ERROR: Unable to read battery info\n"); -static int acpi_battery_info_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_battery_read_info, PDE(inode)->data); + return result; } -static int acpi_battery_read_state(struct seq_file *seq, void *offset) +static int acpi_battery_print_state(struct seq_file *seq, int result) { - int result = 0; struct acpi_battery *battery = seq->private; - struct acpi_battery_status *bst = NULL; + struct acpi_battery_state *bst = NULL; char *units = "?"; - - if (!battery) + if (result) goto end; - acpi_battery_check_present(battery); - - if (battery->flags.present) + if (acpi_battery_present(battery)) seq_printf(seq, "present: yes\n"); else { seq_printf(seq, "present: no\n"); goto end; } - /* Battery Units */ - - units = - battery->flags. - power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; - - /* Battery Status (_BST) */ - - result = acpi_battery_get_status(battery, &bst); - if (result || !bst) { - seq_printf(seq, "ERROR: Unable to read battery status\n"); + bst = battery->bst_data.pointer; + if (!bst) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BST buffer is NULL")); + result = -ENODEV; goto end; } + /* Battery Units */ + + units = acpi_battery_power_units(battery); + if (!(bst->state & 0x04)) seq_printf(seq, "capacity state: ok\n"); else @@ -490,48 +600,43 @@ static int acpi_battery_read_state(struct seq_file *seq, void *offset) (u32) bst->present_voltage); end: - kfree(bst); - return 0; -} + if (result) { + seq_printf(seq, "ERROR: Unable to read battery state\n"); + } -static int acpi_battery_state_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_battery_read_state, PDE(inode)->data); + return result; } -static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) +static int acpi_battery_print_alarm(struct seq_file *seq, int result) { struct acpi_battery *battery = seq->private; char *units = "?"; - - if (!battery) + if (result) goto end; - acpi_battery_check_present(battery); - - if (!battery->flags.present) { + if (!acpi_battery_present(battery)) { seq_printf(seq, "present: no\n"); goto end; } /* Battery Units */ - units = - battery->flags. - power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; - - /* Battery Alarm */ + units = acpi_battery_power_units(battery); seq_printf(seq, "alarm: "); if (!battery->alarm) seq_printf(seq, "unsupported\n"); else - seq_printf(seq, "%d %sh\n", (u32) battery->alarm, units); + seq_printf(seq, "%lu %sh\n", battery->alarm, units); end: - return 0; + + if (result) + seq_printf(seq, "ERROR: Unable to read battery alarm\n"); + + return result; } static ssize_t @@ -543,27 +648,113 @@ acpi_battery_write_alarm(struct file *file, char alarm_string[12] = { '\0' }; struct seq_file *m = file->private_data; struct acpi_battery *battery = m->private; - + int update_result = ACPI_BATTERY_NONE_UPDATE; if (!battery || (count > sizeof(alarm_string) - 1)) return -EINVAL; - acpi_battery_check_present(battery); + mutex_lock(&battery->mutex); - if (!battery->flags.present) - return -ENODEV; + result = acpi_battery_update(battery, 1, &update_result); + if (result) { + result = -ENODEV; + goto end; + } + + if (!acpi_battery_present(battery)) { + result = -ENODEV; + goto end; + } - if (copy_from_user(alarm_string, buffer, count)) - return -EFAULT; + if (copy_from_user(alarm_string, buffer, count)) { + result = -EFAULT; + goto end; + } alarm_string[count] = '\0'; result = acpi_battery_set_alarm(battery, simple_strtoul(alarm_string, NULL, 0)); if (result) - return result; + goto end; + + end: - return count; + acpi_battery_check_result(battery, result); + + if (!result) + result = count; + + mutex_unlock(&battery->mutex); + + return result; +} + +typedef int(*print_func)(struct seq_file *seq, int result); +typedef int(*get_func)(struct acpi_battery *battery); + +static struct acpi_read_mux { + print_func print; + get_func get; +} acpi_read_funcs[ACPI_BATTERY_NUMFILES] = { + {.get = acpi_battery_get_info, .print = acpi_battery_print_info}, + {.get = acpi_battery_get_state, .print = acpi_battery_print_state}, + {.get = acpi_battery_get_alarm, .print = acpi_battery_print_alarm}, +}; + +static int acpi_battery_read(int fid, struct seq_file *seq) +{ + struct acpi_battery *battery = seq->private; + int result = 0; + int update_result = ACPI_BATTERY_NONE_UPDATE; + int update = 0; + + mutex_lock(&battery->mutex); + + update = (get_seconds() - battery->update_time[fid] >= update_time); + update = (update | battery->flags.update[fid]); + + result = acpi_battery_update(battery, update, &update_result); + if (result) + goto end; + + if (update_result == ACPI_BATTERY_EASY_UPDATE) { + result = acpi_read_funcs[fid].get(battery); + if (result) + goto end; + } + + end: + result = acpi_read_funcs[fid].print(seq, result); + acpi_battery_check_result(battery, result); + battery->flags.update[fid] = result; + mutex_unlock(&battery->mutex); + return result; +} + +static int acpi_battery_read_info(struct seq_file *seq, void *offset) +{ + return acpi_battery_read(ACPI_BATTERY_INFO, seq); +} + +static int acpi_battery_read_state(struct seq_file *seq, void *offset) +{ + return acpi_battery_read(ACPI_BATTERY_STATE, seq); +} + +static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) +{ + return acpi_battery_read(ACPI_BATTERY_ALARM, seq); +} + +static int acpi_battery_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_battery_read_info, PDE(inode)->data); +} + +static int acpi_battery_state_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_battery_read_state, PDE(inode)->data); } static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file) @@ -571,35 +762,51 @@ static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file) return single_open(file, acpi_battery_read_alarm, PDE(inode)->data); } -static const struct file_operations acpi_battery_info_ops = { +static struct battery_file { + struct file_operations ops; + mode_t mode; + char *name; +} acpi_battery_file[] = { + { + .name = "info", + .mode = S_IRUGO, + .ops = { .open = acpi_battery_info_open_fs, .read = seq_read, .llseek = seq_lseek, .release = single_release, .owner = THIS_MODULE, -}; - -static const struct file_operations acpi_battery_state_ops = { + }, + }, + { + .name = "state", + .mode = S_IRUGO, + .ops = { .open = acpi_battery_state_open_fs, .read = seq_read, .llseek = seq_lseek, .release = single_release, .owner = THIS_MODULE, -}; - -static const struct file_operations acpi_battery_alarm_ops = { + }, + }, + { + .name = "alarm", + .mode = S_IFREG | S_IRUGO | S_IWUSR, + .ops = { .open = acpi_battery_alarm_open_fs, .read = seq_read, .write = acpi_battery_write_alarm, .llseek = seq_lseek, .release = single_release, .owner = THIS_MODULE, + }, + }, }; static int acpi_battery_add_fs(struct acpi_device *device) { struct proc_dir_entry *entry = NULL; - + int i; if (!acpi_device_dir(device)) { acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), @@ -609,38 +816,16 @@ static int acpi_battery_add_fs(struct acpi_device *device) acpi_device_dir(device)->owner = THIS_MODULE; } - /* 'info' [R] */ - entry = create_proc_entry(ACPI_BATTERY_FILE_INFO, - S_IRUGO, acpi_device_dir(device)); - if (!entry) - return -ENODEV; - else { - entry->proc_fops = &acpi_battery_info_ops; - entry->data = acpi_driver_data(device); - entry->owner = THIS_MODULE; - } - - /* 'status' [R] */ - entry = create_proc_entry(ACPI_BATTERY_FILE_STATUS, - S_IRUGO, acpi_device_dir(device)); - if (!entry) - return -ENODEV; - else { - entry->proc_fops = &acpi_battery_state_ops; - entry->data = acpi_driver_data(device); - entry->owner = THIS_MODULE; - } - - /* 'alarm' [R/W] */ - entry = create_proc_entry(ACPI_BATTERY_FILE_ALARM, - S_IFREG | S_IRUGO | S_IWUSR, - acpi_device_dir(device)); - if (!entry) - return -ENODEV; - else { - entry->proc_fops = &acpi_battery_alarm_ops; - entry->data = acpi_driver_data(device); - entry->owner = THIS_MODULE; + for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) { + entry = create_proc_entry(acpi_battery_file[i].name, + acpi_battery_file[i].mode, acpi_device_dir(device)); + if (!entry) + return -ENODEV; + else { + entry->proc_fops = &acpi_battery_file[i].ops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } } return 0; @@ -648,15 +833,12 @@ static int acpi_battery_add_fs(struct acpi_device *device) static int acpi_battery_remove_fs(struct acpi_device *device) { - + int i; if (acpi_device_dir(device)) { - remove_proc_entry(ACPI_BATTERY_FILE_ALARM, + for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) { + remove_proc_entry(acpi_battery_file[i].name, acpi_device_dir(device)); - remove_proc_entry(ACPI_BATTERY_FILE_STATUS, - acpi_device_dir(device)); - remove_proc_entry(ACPI_BATTERY_FILE_INFO, - acpi_device_dir(device)); - + } remove_proc_entry(acpi_device_bid(device), acpi_battery_dir); acpi_device_dir(device) = NULL; } @@ -673,7 +855,6 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) struct acpi_battery *battery = data; struct acpi_device *device = NULL; - if (!battery) return; @@ -684,8 +865,10 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) case ACPI_BATTERY_NOTIFY_INFO: case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: - acpi_battery_check(battery); - acpi_bus_generate_event(device, event, battery->flags.present); + device = battery->device; + acpi_battery_notify_update(battery); + acpi_bus_generate_event(device, event, + acpi_battery_present(battery)); break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -702,7 +885,6 @@ static int acpi_battery_add(struct acpi_device *device) acpi_status status = 0; struct acpi_battery *battery = NULL; - if (!device) return -EINVAL; @@ -710,15 +892,21 @@ static int acpi_battery_add(struct acpi_device *device) if (!battery) return -ENOMEM; + mutex_init(&battery->mutex); + + mutex_lock(&battery->mutex); + battery->device = device; strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS); acpi_driver_data(device) = battery; - result = acpi_battery_check(battery); + result = acpi_battery_get_status(battery); if (result) goto end; + battery->flags.init_update = 1; + result = acpi_battery_add_fs(device); if (result) goto end; @@ -727,6 +915,7 @@ static int acpi_battery_add(struct acpi_device *device) ACPI_ALL_NOTIFY, acpi_battery_notify, battery); if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler")); result = -ENODEV; goto end; } @@ -736,11 +925,14 @@ static int acpi_battery_add(struct acpi_device *device) device->status.battery_present ? "present" : "absent"); end: + if (result) { acpi_battery_remove_fs(device); kfree(battery); } + mutex_unlock(&battery->mutex); + return result; } @@ -749,18 +941,27 @@ static int acpi_battery_remove(struct acpi_device *device, int type) acpi_status status = 0; struct acpi_battery *battery = NULL; - if (!device || !acpi_driver_data(device)) return -EINVAL; battery = acpi_driver_data(device); + mutex_lock(&battery->mutex); + status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_battery_notify); acpi_battery_remove_fs(device); + kfree(battery->bif_data.pointer); + + kfree(battery->bst_data.pointer); + + mutex_unlock(&battery->mutex); + + mutex_destroy(&battery->mutex); + kfree(battery); return 0; @@ -775,7 +976,10 @@ static int acpi_battery_resume(struct acpi_device *device) return -EINVAL; battery = device->driver_data; - return acpi_battery_check(battery); + + battery->flags.init_update = 1; + + return 0; } static int __init acpi_battery_init(void) @@ -800,7 +1004,6 @@ static int __init acpi_battery_init(void) static void __exit acpi_battery_exit(void) { - acpi_bus_unregister_driver(&acpi_battery_driver); acpi_unlock_battery_dir(acpi_battery_dir); diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c index fb3f31b5e69..56a5b3fffeb 100644 --- a/drivers/acpi/bay.c +++ b/drivers/acpi/bay.c @@ -288,6 +288,11 @@ static int bay_add(acpi_handle handle, int id) new_bay->pdev = pdev; platform_set_drvdata(pdev, new_bay); + /* + * we want the bay driver to be able to send uevents + */ + pdev->dev.uevent_suppress = 0; + if (acpi_bay_add_fs(new_bay)) { platform_device_unregister(new_bay->pdev); goto bay_add_err; @@ -328,18 +333,12 @@ static void bay_notify(acpi_handle handle, u32 event, void *data) { struct bay *bay_dev = (struct bay *)data; struct device *dev = &bay_dev->pdev->dev; + char event_string[12]; + char *envp[] = { event_string, NULL }; bay_dprintk(handle, "Bay event"); - - switch(event) { - case ACPI_NOTIFY_BUS_CHECK: - case ACPI_NOTIFY_DEVICE_CHECK: - case ACPI_NOTIFY_EJECT_REQUEST: - kobject_uevent(&dev->kobj, KOBJ_CHANGE); - break; - default: - printk(KERN_ERR PREFIX "Bay: unknown event %d\n", event); - } + sprintf(event_string, "BAY_EVENT=%d\n", event); + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); } static acpi_status diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index e5084ececb6..6b2658c9624 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -292,6 +292,10 @@ int acpi_bus_generate_event(struct acpi_device *device, u8 type, int data) if (!device) return -EINVAL; + if (acpi_bus_generate_genetlink_event(device, type, data)) + printk(KERN_WARNING PREFIX + "Failed to generate an ACPI event via genetlink!\n"); + /* drop event on the floor if no one's listening */ if (!event_is_open) return 0; diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 4546bf873ae..6192c8be66d 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -40,8 +40,15 @@ MODULE_AUTHOR("Kristen Carlson Accardi"); MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION); MODULE_LICENSE("GPL"); +static int immediate_undock = 1; +module_param(immediate_undock, bool, 0644); +MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to " + "undock immediately when the undock button is pressed, 0 will cause" + " the driver to wait for userspace to write the undock sysfs file " + " before undocking"); + static struct atomic_notifier_head dock_notifier_list; -static struct platform_device dock_device; +static struct platform_device *dock_device; static char dock_device_name[] = "dock"; struct dock_station { @@ -63,6 +70,7 @@ struct dock_dependent_device { }; #define DOCK_DOCKING 0x00000001 +#define DOCK_UNDOCKING 0x00000002 #define DOCK_EVENT 3 #define UNDOCK_EVENT 2 @@ -327,12 +335,20 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) static void dock_event(struct dock_station *ds, u32 event, int num) { - struct device *dev = &dock_device.dev; + struct device *dev = &dock_device->dev; + char event_string[7]; + char *envp[] = { event_string, NULL }; + + if (num == UNDOCK_EVENT) + sprintf(event_string, "UNDOCK"); + else + sprintf(event_string, "DOCK"); + /* * Indicate that the status of the dock station has * changed. */ - kobject_uevent(&dev->kobj, KOBJ_CHANGE); + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); } /** @@ -380,12 +396,11 @@ static void handle_dock(struct dock_station *ds, int dock) union acpi_object arg; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer); - obj = name_buffer.pointer; - printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking"); + printk(KERN_INFO PREFIX "%s - %s\n", + (char *)name_buffer.pointer, dock ? "docking" : "undocking"); /* _DCK method has one argument */ arg_list.count = 1; @@ -394,7 +409,8 @@ static void handle_dock(struct dock_station *ds, int dock) arg.integer.value = dock; status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer); if (ACPI_FAILURE(status)) - pr_debug("%s: failed to execute _DCK\n", obj->string.pointer); + printk(KERN_ERR PREFIX "%s - failed to execute _DCK\n", + (char *)name_buffer.pointer); kfree(buffer.pointer); kfree(name_buffer.pointer); } @@ -420,6 +436,16 @@ static inline void complete_dock(struct dock_station *ds) ds->last_dock_time = jiffies; } +static inline void begin_undock(struct dock_station *ds) +{ + ds->flags |= DOCK_UNDOCKING; +} + +static inline void complete_undock(struct dock_station *ds) +{ + ds->flags &= ~(DOCK_UNDOCKING); +} + /** * dock_in_progress - see if we are in the middle of handling a dock event * @ds: the dock station @@ -550,7 +576,7 @@ static int handle_eject_request(struct dock_station *ds, u32 event) printk(KERN_ERR PREFIX "Unable to undock!\n"); return -EBUSY; } - + complete_undock(ds); return 0; } @@ -594,7 +620,11 @@ static void dock_notify(acpi_handle handle, u32 event, void *data) * to the driver who wish to hotplug. */ case ACPI_NOTIFY_EJECT_REQUEST: - handle_eject_request(ds, event); + begin_undock(ds); + if (immediate_undock) + handle_eject_request(ds, event); + else + dock_event(ds, event, UNDOCK_EVENT); break; default: printk(KERN_ERR PREFIX "Unknown dock event %d\n", event); @@ -653,6 +683,17 @@ static ssize_t show_docked(struct device *dev, DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL); /* + * show_flags - read method for flags file in sysfs + */ +static ssize_t show_flags(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags); + +} +DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL); + +/* * write_undock - write method for "undock" file in sysfs */ static ssize_t write_undock(struct device *dev, struct device_attribute *attr, @@ -675,16 +716,15 @@ static ssize_t show_dock_uid(struct device *dev, struct device_attribute *attr, char *buf) { unsigned long lbuf; - acpi_status status = acpi_evaluate_integer(dock_station->handle, "_UID", NULL, &lbuf); - if(ACPI_FAILURE(status)) { + acpi_status status = acpi_evaluate_integer(dock_station->handle, + "_UID", NULL, &lbuf); + if (ACPI_FAILURE(status)) return 0; - } + return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf); } DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL); - - /** * dock_add - add a new dock station * @handle: the dock station handle @@ -711,33 +751,53 @@ static int dock_add(acpi_handle handle) ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); /* initialize platform device stuff */ - dock_device.name = dock_device_name; - ret = platform_device_register(&dock_device); + dock_device = + platform_device_register_simple(dock_device_name, 0, NULL, 0); + if (IS_ERR(dock_device)) { + kfree(dock_station); + dock_station = NULL; + return PTR_ERR(dock_device); + } + + /* we want the dock device to send uevents */ + dock_device->dev.uevent_suppress = 0; + + ret = device_create_file(&dock_device->dev, &dev_attr_docked); if (ret) { - printk(KERN_ERR PREFIX "Error %d registering dock device\n", ret); + printk("Error %d adding sysfs file\n", ret); + platform_device_unregister(dock_device); kfree(dock_station); + dock_station = NULL; return ret; } - ret = device_create_file(&dock_device.dev, &dev_attr_docked); + ret = device_create_file(&dock_device->dev, &dev_attr_undock); if (ret) { printk("Error %d adding sysfs file\n", ret); - platform_device_unregister(&dock_device); + device_remove_file(&dock_device->dev, &dev_attr_docked); + platform_device_unregister(dock_device); kfree(dock_station); + dock_station = NULL; return ret; } - ret = device_create_file(&dock_device.dev, &dev_attr_undock); + ret = device_create_file(&dock_device->dev, &dev_attr_uid); if (ret) { printk("Error %d adding sysfs file\n", ret); - device_remove_file(&dock_device.dev, &dev_attr_docked); - platform_device_unregister(&dock_device); + device_remove_file(&dock_device->dev, &dev_attr_docked); + device_remove_file(&dock_device->dev, &dev_attr_undock); + platform_device_unregister(dock_device); kfree(dock_station); + dock_station = NULL; return ret; } - ret = device_create_file(&dock_device.dev, &dev_attr_uid); + ret = device_create_file(&dock_device->dev, &dev_attr_flags); if (ret) { printk("Error %d adding sysfs file\n", ret); - platform_device_unregister(&dock_device); + device_remove_file(&dock_device->dev, &dev_attr_docked); + device_remove_file(&dock_device->dev, &dev_attr_undock); + device_remove_file(&dock_device->dev, &dev_attr_uid); + platform_device_unregister(dock_device); kfree(dock_station); + dock_station = NULL; return ret; } @@ -750,6 +810,7 @@ static int dock_add(acpi_handle handle) dd = alloc_dock_dependent_device(handle); if (!dd) { kfree(dock_station); + dock_station = NULL; ret = -ENOMEM; goto dock_add_err_unregister; } @@ -773,10 +834,13 @@ static int dock_add(acpi_handle handle) dock_add_err: kfree(dd); dock_add_err_unregister: - device_remove_file(&dock_device.dev, &dev_attr_docked); - device_remove_file(&dock_device.dev, &dev_attr_undock); - platform_device_unregister(&dock_device); + device_remove_file(&dock_device->dev, &dev_attr_docked); + device_remove_file(&dock_device->dev, &dev_attr_undock); + device_remove_file(&dock_device->dev, &dev_attr_uid); + device_remove_file(&dock_device->dev, &dev_attr_flags); + platform_device_unregister(dock_device); kfree(dock_station); + dock_station = NULL; return ret; } @@ -804,12 +868,15 @@ static int dock_remove(void) printk(KERN_ERR "Error removing notify handler\n"); /* cleanup sysfs */ - device_remove_file(&dock_device.dev, &dev_attr_docked); - device_remove_file(&dock_device.dev, &dev_attr_undock); - platform_device_unregister(&dock_device); + device_remove_file(&dock_device->dev, &dev_attr_docked); + device_remove_file(&dock_device->dev, &dev_attr_undock); + device_remove_file(&dock_device->dev, &dev_attr_uid); + device_remove_file(&dock_device->dev, &dev_attr_flags); + platform_device_unregister(dock_device); /* free dock station memory */ kfree(dock_station); + dock_station = NULL; return 0; } diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 82f496c0767..10e851021ec 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -34,25 +34,26 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/interrupt.h> +#include <linux/list.h> #include <asm/io.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> #include <acpi/actypes.h> -#define _COMPONENT ACPI_EC_COMPONENT -ACPI_MODULE_NAME("ec"); -#define ACPI_EC_COMPONENT 0x00100000 #define ACPI_EC_CLASS "embedded_controller" #define ACPI_EC_HID "PNP0C09" #define ACPI_EC_DEVICE_NAME "Embedded Controller" #define ACPI_EC_FILE_INFO "info" + #undef PREFIX #define PREFIX "ACPI: EC: " + /* EC status register */ #define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ #define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ #define ACPI_EC_FLAG_BURST 0x10 /* burst mode */ #define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */ + /* EC commands */ enum ec_command { ACPI_EC_COMMAND_READ = 0x80, @@ -61,6 +62,7 @@ enum ec_command { ACPI_EC_BURST_DISABLE = 0x83, ACPI_EC_COMMAND_QUERY = 0x84, }; + /* EC events */ enum ec_event { ACPI_EC_EVENT_OBF_1 = 1, /* Output buffer full */ @@ -94,6 +96,16 @@ static struct acpi_driver acpi_ec_driver = { /* 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); + +struct acpi_ec_query_handler { + struct list_head node; + acpi_ec_query_func func; + acpi_handle handle; + void *data; + u8 query_bit; +}; + static struct acpi_ec { acpi_handle handle; unsigned long gpe; @@ -104,6 +116,7 @@ static struct acpi_ec { atomic_t query_pending; atomic_t event_count; wait_queue_head_t wait; + struct list_head list; } *boot_ec, *first_ec; /* -------------------------------------------------------------------------- @@ -245,7 +258,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, 0, 0); if (status) { - printk(KERN_DEBUG PREFIX + printk(KERN_ERR PREFIX "input buffer is not empty, aborting transaction\n"); goto end; } @@ -394,21 +407,67 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) /* -------------------------------------------------------------------------- Event Management -------------------------------------------------------------------------- */ +int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, + acpi_handle handle, acpi_ec_query_func func, + void *data) +{ + struct acpi_ec_query_handler *handler = + kzalloc(sizeof(struct acpi_ec_query_handler), GFP_KERNEL); + if (!handler) + return -ENOMEM; + + handler->query_bit = query_bit; + handler->handle = handle; + handler->func = func; + handler->data = data; + mutex_lock(&ec->lock); + list_add_tail(&handler->node, &ec->list); + mutex_unlock(&ec->lock); + return 0; +} + +EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler); + +void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) +{ + struct acpi_ec_query_handler *handler; + mutex_lock(&ec->lock); + list_for_each_entry(handler, &ec->list, node) { + if (query_bit == handler->query_bit) { + list_del(&handler->node); + kfree(handler); + break; + } + } + mutex_unlock(&ec->lock); +} + +EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); static void acpi_ec_gpe_query(void *ec_cxt) { struct acpi_ec *ec = ec_cxt; u8 value = 0; - char object_name[8]; + struct acpi_ec_query_handler *handler, copy; if (!ec || acpi_ec_query(ec, &value)) return; - - snprintf(object_name, 8, "_Q%2.2X", value); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s", object_name)); - - acpi_evaluate_object(ec->handle, object_name, NULL, NULL); + mutex_lock(&ec->lock); + list_for_each_entry(handler, &ec->list, node) { + if (value == handler->query_bit) { + /* have custom handler for this bit */ + memcpy(©, handler, sizeof(copy)); + mutex_unlock(&ec->lock); + if (copy.func) { + copy.func(copy.data); + } else if (copy.handle) { + acpi_evaluate_object(copy.handle, NULL, NULL, NULL); + } + return; + } + } + mutex_unlock(&ec->lock); + printk(KERN_ERR PREFIX "Handler for query 0x%x is not found!\n", value); } static u32 acpi_ec_gpe_handler(void *data) @@ -427,8 +486,7 @@ static u32 acpi_ec_gpe_handler(void *data) if ((value & ACPI_EC_FLAG_SCI) && !atomic_read(&ec->query_pending)) { atomic_set(&ec->query_pending, 1); status = - acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, - ec); + acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec); } return status == AE_OK ? @@ -454,57 +512,35 @@ acpi_ec_space_setup(acpi_handle region_handle, } static acpi_status -acpi_ec_space_handler(u32 function, - acpi_physical_address address, - u32 bit_width, - acpi_integer * value, +acpi_ec_space_handler(u32 function, acpi_physical_address address, + u32 bits, acpi_integer *value, void *handler_context, void *region_context) { - int result = 0; struct acpi_ec *ec = handler_context; - u64 temp = *value; - acpi_integer f_v = 0; - int i = 0; + int result = 0, i = 0; + u8 temp = 0; if ((address > 0xFF) || !value || !handler_context) return AE_BAD_PARAMETER; - if (bit_width != 8 && acpi_strict) { + if (function != ACPI_READ && function != ACPI_WRITE) return AE_BAD_PARAMETER; - } - - next_byte: - switch (function) { - case ACPI_READ: - temp = 0; - result = acpi_ec_read(ec, (u8) address, (u8 *) & temp); - break; - case ACPI_WRITE: - result = acpi_ec_write(ec, (u8) address, (u8) temp); - break; - default: - result = -EINVAL; - goto out; - break; - } - bit_width -= 8; - if (bit_width) { - if (function == ACPI_READ) - f_v |= temp << 8 * i; - if (function == ACPI_WRITE) - temp >>= 8; - i++; - address++; - goto next_byte; - } + if (bits != 8 && acpi_strict) + return AE_BAD_PARAMETER; - if (function == ACPI_READ) { - f_v |= temp << 8 * i; - *value = f_v; + while (bits - i > 0) { + if (function == ACPI_READ) { + result = acpi_ec_read(ec, address, &temp); + (*value) |= ((acpi_integer)temp) << i; + } else { + temp = 0xff & ((*value) >> i); + result = acpi_ec_write(ec, address, temp); + } + i += 8; + ++address; } - out: switch (result) { case -EINVAL: return AE_BAD_PARAMETER; @@ -597,9 +633,6 @@ static int acpi_ec_remove_fs(struct acpi_device *device) static acpi_status ec_parse_io_ports(struct acpi_resource *resource, void *context); -static acpi_status -ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval); - static struct acpi_ec *make_acpi_ec(void) { struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); @@ -610,13 +643,52 @@ static struct acpi_ec *make_acpi_ec(void) atomic_set(&ec->event_count, 1); mutex_init(&ec->lock); init_waitqueue_head(&ec->wait); + INIT_LIST_HEAD(&ec->list); return ec; } +static acpi_status +acpi_ec_register_query_methods(acpi_handle handle, u32 level, + void *context, void **return_value) +{ + struct acpi_namespace_node *node = handle; + struct acpi_ec *ec = context; + int value = 0; + if (sscanf(node->name.ascii, "_Q%x", &value) == 1) { + acpi_ec_add_query_handler(ec, value, handle, NULL, NULL); + } + return AE_OK; +} + +static int ec_parse_device(struct acpi_ec *ec, acpi_handle handle) +{ + if (ACPI_FAILURE(acpi_walk_resources(handle, METHOD_NAME__CRS, + ec_parse_io_ports, ec))) + return -EINVAL; + + /* Get GPE bit assignment (EC events). */ + /* TODO: Add support for _GPE returning a package */ + if (ACPI_FAILURE(acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe))) + return -EINVAL; + + /* Use the global lock for all EC transactions? */ + acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); + + /* Find and register all query methods */ + acpi_walk_namespace(ACPI_TYPE_METHOD, handle, 1, + acpi_ec_register_query_methods, ec, NULL); + + ec->handle = handle; + + printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx", + ec->gpe, ec->command_addr, ec->data_addr); + + return 0; +} + static int acpi_ec_add(struct acpi_device *device) { - acpi_status status = AE_OK; struct acpi_ec *ec = NULL; if (!device) @@ -629,8 +701,7 @@ static int acpi_ec_add(struct acpi_device *device) if (!ec) return -ENOMEM; - status = ec_parse_device(device->handle, 0, ec, NULL); - if (status != AE_CTRL_TERMINATE) { + if (ec_parse_device(ec, device->handle)) { kfree(ec); return -EINVAL; } @@ -641,6 +712,8 @@ static int acpi_ec_add(struct acpi_device *device) /* We might have incorrect info for GL at boot time */ mutex_lock(&boot_ec->lock); boot_ec->global_lock = ec->global_lock; + /* Copy handlers from new ec into boot ec */ + list_splice(&ec->list, &boot_ec->list); mutex_unlock(&boot_ec->lock); kfree(ec); ec = boot_ec; @@ -651,22 +724,24 @@ static int acpi_ec_add(struct acpi_device *device) acpi_driver_data(device) = ec; acpi_ec_add_fs(device); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", - acpi_device_name(device), acpi_device_bid(device), - (u32) ec->gpe)); - return 0; } static int acpi_ec_remove(struct acpi_device *device, int type) { struct acpi_ec *ec; + struct acpi_ec_query_handler *handler; if (!device) return -EINVAL; ec = acpi_driver_data(device); + mutex_lock(&ec->lock); + list_for_each_entry(handler, &ec->list, node) { + list_del(&handler->node); + kfree(handler); + } + mutex_unlock(&ec->lock); acpi_ec_remove_fs(device); acpi_driver_data(device) = NULL; if (ec == first_ec) @@ -722,15 +797,13 @@ static int ec_install_handlers(struct acpi_ec *ec) return -ENODEV; } - /* EC is fully operational, allow queries */ - atomic_set(&ec->query_pending, 0); - return 0; } static int acpi_ec_start(struct acpi_device *device) { struct acpi_ec *ec; + int ret = 0; if (!device) return -EINVAL; @@ -740,14 +813,14 @@ static int acpi_ec_start(struct acpi_device *device) if (!ec) return -EINVAL; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", - ec->gpe, ec->command_addr, ec->data_addr)); - /* Boot EC is already working */ - if (ec == boot_ec) - return 0; + if (ec != boot_ec) + ret = ec_install_handlers(ec); + + /* EC is fully operational, allow queries */ + atomic_set(&ec->query_pending, 0); - return ec_install_handlers(ec); + return ret; } static int acpi_ec_stop(struct acpi_device *device, int type) @@ -779,34 +852,6 @@ static int acpi_ec_stop(struct acpi_device *device, int type) return 0; } -static acpi_status -ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) -{ - acpi_status status; - - struct acpi_ec *ec = context; - status = acpi_walk_resources(handle, METHOD_NAME__CRS, - ec_parse_io_ports, ec); - if (ACPI_FAILURE(status)) - return status; - - /* Get GPE bit assignment (EC events). */ - /* TODO: Add support for _GPE returning a package */ - status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); - if (ACPI_FAILURE(status)) - return status; - - /* Use the global lock for all EC transactions? */ - acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); - - ec->handle = handle; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", - ec->gpe, ec->command_addr, ec->data_addr)); - - return AE_CTRL_TERMINATE; -} - int __init acpi_ec_ecdt_probe(void) { int ret; @@ -825,7 +870,7 @@ int __init acpi_ec_ecdt_probe(void) if (ACPI_FAILURE(status)) goto error; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); + printk(KERN_INFO PREFIX "EC description table is found, configuring boot EC\n"); boot_ec->command_addr = ecdt_ptr->control.address; boot_ec->data_addr = ecdt_ptr->data.address; diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index 3b23562e6f9..dfa5853b17f 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c @@ -11,6 +11,8 @@ #include <linux/init.h> #include <linux/poll.h> #include <acpi/acpi_drivers.h> +#include <net/netlink.h> +#include <net/genetlink.h> #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME("event"); @@ -48,7 +50,6 @@ acpi_system_read_event(struct file *file, char __user * buffer, size_t count, static int chars_remaining = 0; static char *ptr; - if (!chars_remaining) { memset(&event, 0, sizeof(struct acpi_bus_event)); @@ -106,23 +107,161 @@ static const struct file_operations acpi_system_event_ops = { .poll = acpi_system_poll_event, }; +#ifdef CONFIG_NET +unsigned int acpi_event_seqnum; +struct acpi_genl_event { + acpi_device_class device_class; + char bus_id[15]; + u32 type; + u32 data; +}; + +/* attributes of acpi_genl_family */ +enum { + ACPI_GENL_ATTR_UNSPEC, + ACPI_GENL_ATTR_EVENT, /* ACPI event info needed by user space */ + __ACPI_GENL_ATTR_MAX, +}; +#define ACPI_GENL_ATTR_MAX (__ACPI_GENL_ATTR_MAX - 1) + +/* commands supported by the acpi_genl_family */ +enum { + ACPI_GENL_CMD_UNSPEC, + ACPI_GENL_CMD_EVENT, /* kernel->user notifications for ACPI events */ + __ACPI_GENL_CMD_MAX, +}; +#define ACPI_GENL_CMD_MAX (__ACPI_GENL_CMD_MAX - 1) + +#define ACPI_GENL_FAMILY_NAME "acpi_event" +#define ACPI_GENL_VERSION 0x01 +#define ACPI_GENL_MCAST_GROUP_NAME "acpi_mc_group" + +static struct genl_family acpi_event_genl_family = { + .id = GENL_ID_GENERATE, + .name = ACPI_GENL_FAMILY_NAME, + .version = ACPI_GENL_VERSION, + .maxattr = ACPI_GENL_ATTR_MAX, +}; + +static struct genl_multicast_group acpi_event_mcgrp = { + .name = ACPI_GENL_MCAST_GROUP_NAME, +}; + +int acpi_bus_generate_genetlink_event(struct acpi_device *device, + u8 type, int data) +{ + struct sk_buff *skb; + struct nlattr *attr; + struct acpi_genl_event *event; + void *msg_header; + int size; + int result; + + /* allocate memory */ + size = nla_total_size(sizeof(struct acpi_genl_event)) + + nla_total_size(0); + + skb = genlmsg_new(size, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + /* add the genetlink message header */ + msg_header = genlmsg_put(skb, 0, acpi_event_seqnum++, + &acpi_event_genl_family, 0, + ACPI_GENL_CMD_EVENT); + if (!msg_header) { + nlmsg_free(skb); + return -ENOMEM; + } + + /* fill the data */ + attr = + nla_reserve(skb, ACPI_GENL_ATTR_EVENT, + sizeof(struct acpi_genl_event)); + if (!attr) { + nlmsg_free(skb); + return -EINVAL; + } + + event = nla_data(attr); + if (!event) { + nlmsg_free(skb); + return -EINVAL; + } + + memset(event, 0, sizeof(struct acpi_genl_event)); + + strcpy(event->device_class, device->pnp.device_class); + strcpy(event->bus_id, device->dev.bus_id); + event->type = type; + event->data = data; + + /* send multicast genetlink message */ + result = genlmsg_end(skb, msg_header); + if (result < 0) { + nlmsg_free(skb); + return result; + } + + result = + genlmsg_multicast(skb, 0, acpi_event_mcgrp.id, GFP_ATOMIC); + if (result) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Failed to send a Genetlink message!\n")); + return 0; +} + +static int acpi_event_genetlink_init(void) +{ + int result; + + result = genl_register_family(&acpi_event_genl_family); + if (result) + return result; + + result = genl_register_mc_group(&acpi_event_genl_family, + &acpi_event_mcgrp); + if (result) + genl_unregister_family(&acpi_event_genl_family); + + return result; +} + +#else +int acpi_bus_generate_genetlink_event(struct acpi_device *device, u8 type, + int data) +{ + return 0; +} + +static int acpi_event_genetlink_init(void) +{ + return -ENODEV; +} +#endif + static int __init acpi_event_init(void) { struct proc_dir_entry *entry; int error = 0; - if (acpi_disabled) return 0; + /* create genetlink for acpi event */ + error = acpi_event_genetlink_init(); + if (error) + printk(KERN_WARNING PREFIX + "Failed to create genetlink family for ACPI event\n"); + /* 'event' [R] */ entry = create_proc_entry("event", S_IRUSR, acpi_root_dir); if (entry) entry->proc_fops = &acpi_system_event_ops; - else { - error = -ENODEV; - } - return error; + else + return -ENODEV; + + return 0; } -subsys_initcall(acpi_event_init); +fs_initcall(acpi_event_init); diff --git a/drivers/acpi/events/evgpeblk.c b/drivers/acpi/events/evgpeblk.c index 902c287b3a4..361ebe6c4a6 100644 --- a/drivers/acpi/events/evgpeblk.c +++ b/drivers/acpi/events/evgpeblk.c @@ -586,6 +586,10 @@ acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt) flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); if (gpe_xrupt->previous) { gpe_xrupt->previous->next = gpe_xrupt->next; + } else { + /* No previous, update list head */ + + acpi_gbl_gpe_xrupt_list_head = gpe_xrupt->next; } if (gpe_xrupt->next) { diff --git a/drivers/acpi/events/evrgnini.c b/drivers/acpi/events/evrgnini.c index 400d90fca96..23ee7bc4a70 100644 --- a/drivers/acpi/events/evrgnini.c +++ b/drivers/acpi/events/evrgnini.c @@ -284,6 +284,7 @@ acpi_ev_pci_config_region_setup(acpi_handle handle, } if (!pci_device_node) { + ACPI_FREE(pci_id); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 41427a41f62..4893e256e39 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -16,7 +16,7 @@ #if ACPI_GLUE_DEBUG #define DBG(x...) printk(PREFIX x) #else -#define DBG(x...) +#define DBG(x...) do { } while(0) #endif static LIST_HEAD(bus_type_list); static DECLARE_RWSEM(bus_type_sem); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 00d53c2fd1e..12c09fafce9 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -77,13 +77,7 @@ static struct workqueue_struct *kacpi_notify_wq; #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ static char osi_additional_string[OSI_STRING_LENGTH_MAX]; -#define OSI_LINUX_ENABLED -#ifdef OSI_LINUX_ENABLED -int osi_linux = 1; /* enable _OSI(Linux) by default */ -#else -int osi_linux; /* disable _OSI(Linux) by default */ -#endif - +static int osi_linux; /* disable _OSI(Linux) by default */ #ifdef CONFIG_DMI static struct __initdata dmi_system_id acpi_osl_dmi_table[]; @@ -1183,17 +1177,10 @@ acpi_os_validate_interface (char *interface) if (!strcmp("Linux", interface)) { printk(KERN_WARNING PREFIX "System BIOS is requesting _OSI(Linux)\n"); -#ifdef OSI_LINUX_ENABLED - printk(KERN_WARNING PREFIX - "Please test with \"acpi_osi=!Linux\"\n" - "Please send dmidecode " - "to linux-acpi@vger.kernel.org\n"); -#else printk(KERN_WARNING PREFIX "If \"acpi_osi=Linux\" works better,\n" "Please send dmidecode " "to linux-acpi@vger.kernel.org\n"); -#endif if(osi_linux) return AE_OK; } @@ -1227,36 +1214,14 @@ acpi_os_validate_address ( } #ifdef CONFIG_DMI -#ifdef OSI_LINUX_ENABLED -static int dmi_osi_not_linux(struct dmi_system_id *d) -{ - printk(KERN_NOTICE "%s detected: requires not _OSI(Linux)\n", d->ident); - enable_osi_linux(0); - return 0; -} -#else static int dmi_osi_linux(struct dmi_system_id *d) { - printk(KERN_NOTICE "%s detected: requires _OSI(Linux)\n", d->ident); + printk(KERN_NOTICE "%s detected: enabling _OSI(Linux)\n", d->ident); enable_osi_linux(1); return 0; } -#endif static struct dmi_system_id acpi_osl_dmi_table[] __initdata = { -#ifdef OSI_LINUX_ENABLED - /* - * Boxes that need NOT _OSI(Linux) - */ - { - .callback = dmi_osi_not_linux, - .ident = "Toshiba Satellite P100", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_BOARD_NAME, "Satellite P100"), - }, - }, -#else /* * Boxes that need _OSI(Linux) */ @@ -1268,7 +1233,6 @@ static struct dmi_system_id acpi_osl_dmi_table[] __initdata = { DMI_MATCH(DMI_BOARD_NAME, "MPAD-MSAE Customer Reference Boards"), }, }, -#endif {} }; #endif /* CONFIG_DMI */ diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index acc59477137..3448edd61dc 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -733,7 +733,7 @@ static int acpi_pci_link_add(struct acpi_device *device) /* query and set link->irq.active */ acpi_pci_link_get_current(link); - printk(PREFIX "%s [%s] (IRQs", acpi_device_name(device), + printk(KERN_INFO PREFIX "%s [%s] (IRQs", acpi_device_name(device), acpi_device_bid(device)); for (i = 0; i < link->irq.possible_count; i++) { if (link->irq.active == link->irq.possible[i]) { diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index e1ca86dfdd6..81aceb5da7c 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -66,6 +66,7 @@ #define ACPI_PROCESSOR_FILE_LIMIT "limit" #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 #define ACPI_PROCESSOR_NOTIFY_POWER 0x81 +#define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82 #define ACPI_PROCESSOR_LIMIT_USER 0 #define ACPI_PROCESSOR_LIMIT_THERMAL 1 @@ -84,6 +85,8 @@ static int acpi_processor_info_open_fs(struct inode *inode, struct file *file); static void acpi_processor_notify(acpi_handle handle, u32 event, void *data); static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu); static int acpi_processor_handle_eject(struct acpi_processor *pr); +extern int acpi_processor_tstate_has_changed(struct acpi_processor *pr); + static struct acpi_driver acpi_processor_driver = { .name = "processor", @@ -696,6 +699,9 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) acpi_processor_cst_has_changed(pr); acpi_bus_generate_event(device, event, 0); break; + case ACPI_PROCESSOR_NOTIFY_THROTTLING: + acpi_processor_tstate_has_changed(pr); + acpi_bus_generate_event(device, event, 0); default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported event [0x%x]\n", event)); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index bb5d23be426..a898991f77c 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -490,7 +490,17 @@ static void acpi_processor_idle(void) case ACPI_STATE_C3: - if (pr->flags.bm_check) { + /* + * disable bus master + * bm_check implies we need ARB_DIS + * !bm_check implies we need cache flush + * bm_control implies whether we can do ARB_DIS + * + * That leaves a case where bm_check is set and bm_control is + * not set. In that case we cannot do much, we enter C3 + * without doing anything. + */ + if (pr->flags.bm_check && pr->flags.bm_control) { if (atomic_inc_return(&c3_cpu_count) == num_online_cpus()) { /* @@ -499,7 +509,7 @@ static void acpi_processor_idle(void) */ acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); } - } else { + } else if (!pr->flags.bm_check) { /* SMP with no shared cache... Invalidate cache */ ACPI_FLUSH_CPU_CACHE(); } @@ -511,7 +521,7 @@ static void acpi_processor_idle(void) acpi_cstate_enter(cx); /* Get end time (ticks) */ t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); - if (pr->flags.bm_check) { + if (pr->flags.bm_check && pr->flags.bm_control) { /* Enable bus master arbitration */ atomic_dec(&c3_cpu_count); acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0); @@ -961,9 +971,9 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr, if (pr->flags.bm_check) { /* bus mastering control is necessary */ if (!pr->flags.bm_control) { + /* In this case we enter C3 without bus mastering */ ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "C3 support requires bus mastering control\n")); - return; + "C3 support without bus mastering control\n")); } } else { /* diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index b33486009f4..0b8204e7082 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -44,17 +44,251 @@ #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_throttling"); +static int acpi_processor_get_throttling(struct acpi_processor *pr); +int acpi_processor_set_throttling(struct acpi_processor *pr, int state); + +/* + * _TPC - Throttling Present Capabilities + */ +static int acpi_processor_get_platform_limit(struct acpi_processor *pr) +{ + acpi_status status = 0; + unsigned long tpc = 0; + + if (!pr) + return -EINVAL; + status = acpi_evaluate_integer(pr->handle, "_TPC", NULL, &tpc); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _TPC")); + } + return -ENODEV; + } + pr->throttling_platform_limit = (int)tpc; + return 0; +} + +int acpi_processor_tstate_has_changed(struct acpi_processor *pr) +{ + return acpi_processor_get_platform_limit(pr); +} + +/* + * _PTC - Processor Throttling Control (and status) register location + */ +static int acpi_processor_get_throttling_control(struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = 0; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *ptc = NULL; + union acpi_object obj = { 0 }; + + status = acpi_evaluate_object(pr->handle, "_PTC", NULL, &buffer); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PTC")); + } + return -ENODEV; + } + + ptc = (union acpi_object *)buffer.pointer; + if (!ptc || (ptc->type != ACPI_TYPE_PACKAGE) + || (ptc->package.count != 2)) { + printk(KERN_ERR PREFIX "Invalid _PTC data\n"); + result = -EFAULT; + goto end; + } + + /* + * control_register + */ + + obj = ptc->package.elements[0]; + + if ((obj.type != ACPI_TYPE_BUFFER) + || (obj.buffer.length < sizeof(struct acpi_ptc_register)) + || (obj.buffer.pointer == NULL)) { + printk(KERN_ERR PREFIX + "Invalid _PTC data (control_register)\n"); + result = -EFAULT; + goto end; + } + memcpy(&pr->throttling.control_register, obj.buffer.pointer, + sizeof(struct acpi_ptc_register)); + + /* + * status_register + */ + + obj = ptc->package.elements[1]; + + if ((obj.type != ACPI_TYPE_BUFFER) + || (obj.buffer.length < sizeof(struct acpi_ptc_register)) + || (obj.buffer.pointer == NULL)) { + printk(KERN_ERR PREFIX "Invalid _PTC data (status_register)\n"); + result = -EFAULT; + goto end; + } + + memcpy(&pr->throttling.status_register, obj.buffer.pointer, + sizeof(struct acpi_ptc_register)); + + end: + kfree(buffer.pointer); + + return result; +} + +/* + * _TSS - Throttling Supported States + */ +static int acpi_processor_get_throttling_states(struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; + struct acpi_buffer state = { 0, NULL }; + union acpi_object *tss = NULL; + int i; + + status = acpi_evaluate_object(pr->handle, "_TSS", NULL, &buffer); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _TSS")); + } + return -ENODEV; + } + + tss = buffer.pointer; + if (!tss || (tss->type != ACPI_TYPE_PACKAGE)) { + printk(KERN_ERR PREFIX "Invalid _TSS data\n"); + result = -EFAULT; + goto end; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d throttling states\n", + tss->package.count)); + + pr->throttling.state_count = tss->package.count; + pr->throttling.states_tss = + kmalloc(sizeof(struct acpi_processor_tx_tss) * tss->package.count, + GFP_KERNEL); + if (!pr->throttling.states_tss) { + result = -ENOMEM; + goto end; + } + + for (i = 0; i < pr->throttling.state_count; i++) { + + struct acpi_processor_tx_tss *tx = + (struct acpi_processor_tx_tss *)&(pr->throttling. + states_tss[i]); + + state.length = sizeof(struct acpi_processor_tx_tss); + state.pointer = tx; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i)); + + status = acpi_extract_package(&(tss->package.elements[i]), + &format, &state); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Invalid _TSS data")); + result = -EFAULT; + kfree(pr->throttling.states_tss); + goto end; + } + + if (!tx->freqpercentage) { + printk(KERN_ERR PREFIX + "Invalid _TSS data: freq is zero\n"); + result = -EFAULT; + kfree(pr->throttling.states_tss); + goto end; + } + } + + end: + kfree(buffer.pointer); + + return result; +} + +/* + * _TSD - T-State Dependencies + */ +static int acpi_processor_get_tsd(struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; + struct acpi_buffer state = { 0, NULL }; + union acpi_object *tsd = NULL; + struct acpi_tsd_package *pdomain; + + status = acpi_evaluate_object(pr->handle, "_TSD", NULL, &buffer); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _TSD")); + } + return -ENODEV; + } + + tsd = buffer.pointer; + if (!tsd || (tsd->type != ACPI_TYPE_PACKAGE)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _TSD data\n")); + result = -EFAULT; + goto end; + } + + if (tsd->package.count != 1) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _TSD data\n")); + result = -EFAULT; + goto end; + } + + pdomain = &(pr->throttling.domain_info); + + state.length = sizeof(struct acpi_tsd_package); + state.pointer = pdomain; + + status = acpi_extract_package(&(tsd->package.elements[0]), + &format, &state); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _TSD data\n")); + result = -EFAULT; + goto end; + } + + if (pdomain->num_entries != ACPI_TSD_REV0_ENTRIES) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _TSD:num_entries\n")); + result = -EFAULT; + goto end; + } + + if (pdomain->revision != ACPI_TSD_REV0_REVISION) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _TSD:revision\n")); + result = -EFAULT; + goto end; + } + + end: + kfree(buffer.pointer); + return result; +} + /* -------------------------------------------------------------------------- Throttling Control -------------------------------------------------------------------------- */ -static int acpi_processor_get_throttling(struct acpi_processor *pr) +static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr) { int state = 0; u32 value = 0; u32 duty_mask = 0; u32 duty_value = 0; - if (!pr) return -EINVAL; @@ -94,13 +328,115 @@ static int acpi_processor_get_throttling(struct acpi_processor *pr) return 0; } -int acpi_processor_set_throttling(struct acpi_processor *pr, int state) +static int acpi_read_throttling_status(struct acpi_processor_throttling + *throttling) +{ + int value = -1; + switch (throttling->status_register.space_id) { + case ACPI_ADR_SPACE_SYSTEM_IO: + acpi_os_read_port((acpi_io_address) throttling->status_register. + address, &value, + (u32) throttling->status_register.bit_width * + 8); + break; + case ACPI_ADR_SPACE_FIXED_HARDWARE: + printk(KERN_ERR PREFIX + "HARDWARE addr space,NOT supported yet\n"); + break; + default: + printk(KERN_ERR PREFIX "Unknown addr space %d\n", + (u32) (throttling->status_register.space_id)); + } + return value; +} + +static int acpi_write_throttling_state(struct acpi_processor_throttling + *throttling, int value) +{ + int ret = -1; + + switch (throttling->control_register.space_id) { + case ACPI_ADR_SPACE_SYSTEM_IO: + acpi_os_write_port((acpi_io_address) throttling-> + control_register.address, value, + (u32) throttling->control_register. + bit_width * 8); + ret = 0; + break; + case ACPI_ADR_SPACE_FIXED_HARDWARE: + printk(KERN_ERR PREFIX + "HARDWARE addr space,NOT supported yet\n"); + break; + default: + printk(KERN_ERR PREFIX "Unknown addr space %d\n", + (u32) (throttling->control_register.space_id)); + } + return ret; +} + +static int acpi_get_throttling_state(struct acpi_processor *pr, int value) +{ + int i; + + for (i = 0; i < pr->throttling.state_count; i++) { + struct acpi_processor_tx_tss *tx = + (struct acpi_processor_tx_tss *)&(pr->throttling. + states_tss[i]); + if (tx->control == value) + break; + } + if (i > pr->throttling.state_count) + i = -1; + return i; +} + +static int acpi_get_throttling_value(struct acpi_processor *pr, int state) +{ + int value = -1; + if (state >= 0 && state <= pr->throttling.state_count) { + struct acpi_processor_tx_tss *tx = + (struct acpi_processor_tx_tss *)&(pr->throttling. + states_tss[state]); + value = tx->control; + } + return value; +} + +static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr) +{ + int state = 0; + u32 value = 0; + + if (!pr) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; + + pr->throttling.state = 0; + local_irq_disable(); + value = acpi_read_throttling_status(&pr->throttling); + if (value >= 0) { + state = acpi_get_throttling_state(pr, value); + pr->throttling.state = state; + } + local_irq_enable(); + + return 0; +} + +static int acpi_processor_get_throttling(struct acpi_processor *pr) +{ + return pr->throttling.acpi_processor_get_throttling(pr); +} + +static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr, + int state) { u32 value = 0; u32 duty_mask = 0; u32 duty_value = 0; - if (!pr) return -EINVAL; @@ -113,6 +449,8 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) if (state == pr->throttling.state) return 0; + if (state < pr->throttling_platform_limit) + return -EPERM; /* * Calculate the duty_value and duty_mask. */ @@ -165,13 +503,49 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) return 0; } +static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr, + int state) +{ + u32 value = 0; + + if (!pr) + return -EINVAL; + + if ((state < 0) || (state > (pr->throttling.state_count - 1))) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; + + if (state == pr->throttling.state) + return 0; + + if (state < pr->throttling_platform_limit) + return -EPERM; + + local_irq_disable(); + + value = acpi_get_throttling_value(pr, state); + if (value >= 0) { + acpi_write_throttling_state(&pr->throttling, value); + pr->throttling.state = state; + } + local_irq_enable(); + + return 0; +} + +int acpi_processor_set_throttling(struct acpi_processor *pr, int state) +{ + return pr->throttling.acpi_processor_set_throttling(pr, state); +} + int acpi_processor_get_throttling_info(struct acpi_processor *pr) { int result = 0; int step = 0; int i = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n", pr->throttling.address, @@ -181,7 +555,26 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) if (!pr) return -EINVAL; - /* TBD: Support ACPI 2.0 objects */ + /* + * Evaluate _PTC, _TSS and _TPC + * They must all be present or none of them can be used. + */ + if (acpi_processor_get_throttling_control(pr) || + acpi_processor_get_throttling_states(pr) || + acpi_processor_get_platform_limit(pr)) + { + pr->throttling.acpi_processor_get_throttling = + &acpi_processor_get_throttling_fadt; + pr->throttling.acpi_processor_set_throttling = + &acpi_processor_set_throttling_fadt; + } else { + pr->throttling.acpi_processor_get_throttling = + &acpi_processor_get_throttling_ptc; + pr->throttling.acpi_processor_set_throttling = + &acpi_processor_set_throttling_ptc; + } + + acpi_processor_get_tsd(pr); if (!pr->throttling.address) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling register\n")); @@ -262,7 +655,6 @@ static int acpi_processor_throttling_seq_show(struct seq_file *seq, int i = 0; int result = 0; - if (!pr) goto end; @@ -280,15 +672,27 @@ static int acpi_processor_throttling_seq_show(struct seq_file *seq, } seq_printf(seq, "state count: %d\n" - "active state: T%d\n", - pr->throttling.state_count, pr->throttling.state); + "active state: T%d\n" + "state available: T%d to T%d\n", + pr->throttling.state_count, pr->throttling.state, + pr->throttling_platform_limit, + pr->throttling.state_count - 1); seq_puts(seq, "states:\n"); - for (i = 0; i < pr->throttling.state_count; i++) - seq_printf(seq, " %cT%d: %02d%%\n", - (i == pr->throttling.state ? '*' : ' '), i, - (pr->throttling.states[i].performance ? pr-> - throttling.states[i].performance / 10 : 0)); + if (pr->throttling.acpi_processor_get_throttling == + acpi_processor_get_throttling_fadt) { + for (i = 0; i < pr->throttling.state_count; i++) + seq_printf(seq, " %cT%d: %02d%%\n", + (i == pr->throttling.state ? '*' : ' '), i, + (pr->throttling.states[i].performance ? pr-> + throttling.states[i].performance / 10 : 0)); + } else { + for (i = 0; i < pr->throttling.state_count; i++) + seq_printf(seq, " %cT%d: %02d%%\n", + (i == pr->throttling.state ? '*' : ' '), i, + (int)pr->throttling.states_tss[i]. + freqpercentage); + } end: return 0; @@ -301,7 +705,7 @@ static int acpi_processor_throttling_open_fs(struct inode *inode, PDE(inode)->data); } -static ssize_t acpi_processor_write_throttling(struct file * file, +static ssize_t acpi_processor_write_throttling(struct file *file, const char __user * buffer, size_t count, loff_t * data) { @@ -310,7 +714,6 @@ static ssize_t acpi_processor_write_throttling(struct file * file, struct acpi_processor *pr = m->private; char state_string[12] = { '\0' }; - if (!pr || (count > sizeof(state_string) - 1)) return -EINVAL; diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index c1bae106833..974d00ccfe8 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -127,7 +127,7 @@ static int acpi_sbs_resume(struct acpi_device *device); static struct acpi_driver acpi_sbs_driver = { .name = "sbs", .class = ACPI_SBS_CLASS, - .ids = ACPI_SBS_HID, + .ids = "ACPI0001,ACPI0005", .ops = { .add = acpi_sbs_add, .remove = acpi_sbs_remove, @@ -176,10 +176,8 @@ struct acpi_battery { }; struct acpi_sbs { - acpi_handle handle; int base; struct acpi_device *device; - struct acpi_ec_smbus *smbus; struct mutex mutex; int sbsm_present; int sbsm_batteries_supported; @@ -511,7 +509,7 @@ static int acpi_sbsm_get_info(struct acpi_sbs *sbs) "acpi_sbs_read_word() failed")); goto end; } - + sbs->sbsm_present = 1; sbs->sbsm_batteries_supported = battery_system_info & 0x000f; end: @@ -1630,13 +1628,12 @@ static int acpi_sbs_add(struct acpi_device *device) { struct acpi_sbs *sbs = NULL; int result = 0, remove_result = 0; - unsigned long sbs_obj; int id; acpi_status status = AE_OK; unsigned long val; status = - acpi_evaluate_integer(device->parent->handle, "_EC", NULL, &val); + acpi_evaluate_integer(device->handle, "_EC", NULL, &val); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Error obtaining _EC")); return -EIO; @@ -1653,7 +1650,7 @@ static int acpi_sbs_add(struct acpi_device *device) sbs_mutex_lock(sbs); - sbs->base = (val & 0xff00ull) >> 8; + sbs->base = 0xff & (val >> 8); sbs->device = device; strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME); @@ -1665,24 +1662,10 @@ static int acpi_sbs_add(struct acpi_device *device) ACPI_EXCEPTION((AE_INFO, AE_ERROR, "acpi_ac_add() failed")); goto end; } - status = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj); - if (status) { - ACPI_EXCEPTION((AE_INFO, status, - "acpi_evaluate_integer() failed")); - result = -EIO; - goto end; - } - if (sbs_obj > 0) { - result = acpi_sbsm_get_info(sbs); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbsm_get_info() failed")); - goto end; - } - sbs->sbsm_present = 1; - } - if (sbs->sbsm_present == 0) { + acpi_sbsm_get_info(sbs); + + if (!sbs->sbsm_present) { result = acpi_battery_add(sbs, 0); if (result) { ACPI_EXCEPTION((AE_INFO, AE_ERROR, @@ -1702,8 +1685,6 @@ static int acpi_sbs_add(struct acpi_device *device) } } - sbs->handle = device->handle; - init_timer(&sbs->update_timer); result = acpi_check_update_proc(sbs); if (result) diff --git a/drivers/acpi/sleep/Makefile b/drivers/acpi/sleep/Makefile index d6c017709c8..01a993a1d08 100644 --- a/drivers/acpi/sleep/Makefile +++ b/drivers/acpi/sleep/Makefile @@ -1,5 +1,5 @@ obj-y := poweroff.o wakeup.o -obj-$(CONFIG_ACPI_SLEEP) += main.o -obj-$(CONFIG_ACPI_SLEEP_PROC_FS) += proc.o +obj-y += main.o +obj-$(CONFIG_X86) += proc.o EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 29bdb013f46..ab21357c5c7 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -154,10 +154,12 @@ static int acpi_pm_finish(suspend_state_t pm_state) acpi_target_sleep_state = ACPI_STATE_S0; +#ifdef CONFIG_X86 if (init_8259A_after_S1) { printk("Broken toshiba laptop -> kicking interrupts\n"); init_8259A(0); } +#endif return 0; } @@ -229,11 +231,6 @@ static void acpi_hibernation_finish(void) /* reset firmware waking vector */ acpi_set_firmware_waking_vector((acpi_physical_address) 0); - - if (init_8259A_after_S1) { - printk("Broken toshiba laptop -> kicking interrupts\n"); - init_8259A(0); - } } static int acpi_hibernation_pre_restore(void) diff --git a/drivers/acpi/sleep/poweroff.c b/drivers/acpi/sleep/poweroff.c index 39e40d56b03..b3f68ef0669 100644 --- a/drivers/acpi/sleep/poweroff.c +++ b/drivers/acpi/sleep/poweroff.c @@ -18,7 +18,6 @@ int acpi_sleep_prepare(u32 acpi_state) { -#ifdef CONFIG_ACPI_SLEEP /* do we have a wakeup address for S2 and S3? */ if (acpi_state == ACPI_STATE_S3) { if (!acpi_wakeup_address) { @@ -31,7 +30,6 @@ int acpi_sleep_prepare(u32 acpi_state) } ACPI_FLUSH_CPU_CACHE(); acpi_enable_wakeup_device_prep(acpi_state); -#endif acpi_gpe_sleep_prepare(acpi_state); acpi_enter_sleep_state_prep(acpi_state); return 0; diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 61f1822cc35..ed58e1168ae 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -14,8 +14,16 @@ #include "sleep.h" #define _COMPONENT ACPI_SYSTEM_COMPONENT + +/* + * this file provides support for: + * /proc/acpi/sleep + * /proc/acpi/alarm + * /proc/acpi/wakeup + */ + ACPI_MODULE_NAME("sleep") -#ifdef CONFIG_ACPI_SLEEP_PROC_SLEEP +#ifdef CONFIG_ACPI_PROCFS static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset) { int i; @@ -68,7 +76,7 @@ acpi_system_write_sleep(struct file *file, Done: return error ? error : count; } -#endif /* CONFIG_ACPI_SLEEP_PROC_SLEEP */ +#endif /* CONFIG_ACPI_PROCFS */ #if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE) /* use /sys/class/rtc/rtcX/wakealarm instead; it's not ACPI-specific */ @@ -463,7 +471,7 @@ static const struct file_operations acpi_system_wakeup_device_fops = { .release = single_release, }; -#ifdef CONFIG_ACPI_SLEEP_PROC_SLEEP +#ifdef CONFIG_ACPI_PROCFS static const struct file_operations acpi_system_sleep_fops = { .open = acpi_system_sleep_open_fs, .read = seq_read, @@ -471,7 +479,7 @@ static const struct file_operations acpi_system_sleep_fops = { .llseek = seq_lseek, .release = single_release, }; -#endif /* CONFIG_ACPI_SLEEP_PROC_SLEEP */ +#endif /* CONFIG_ACPI_PROCFS */ #ifdef HAVE_ACPI_LEGACY_ALARM static const struct file_operations acpi_system_alarm_fops = { @@ -498,14 +506,14 @@ static int __init acpi_sleep_proc_init(void) if (acpi_disabled) return 0; -#ifdef CONFIG_ACPI_SLEEP_PROC_SLEEP +#ifdef CONFIG_ACPI_PROCFS /* 'sleep' [R/W] */ entry = create_proc_entry("sleep", S_IFREG | S_IRUGO | S_IWUSR, acpi_root_dir); if (entry) entry->proc_fops = &acpi_system_sleep_fops; -#endif +#endif /* CONFIG_ACPI_PROCFS */ #ifdef HAVE_ACPI_LEGACY_ALARM /* 'alarm' [R/W] */ diff --git a/drivers/acpi/sleep/wakeup.c b/drivers/acpi/sleep/wakeup.c index fab8f2694f0..97c27ddb144 100644 --- a/drivers/acpi/sleep/wakeup.c +++ b/drivers/acpi/sleep/wakeup.c @@ -17,7 +17,6 @@ ACPI_MODULE_NAME("wakeup_devices") extern struct list_head acpi_wakeup_device_list; extern spinlock_t acpi_device_lock; -#ifdef CONFIG_ACPI_SLEEP /** * acpi_enable_wakeup_device_prep - prepare wakeup devices * @sleep_state: ACPI state @@ -180,7 +179,6 @@ static int __init acpi_wakeup_device_init(void) } late_initcall(acpi_wakeup_device_init); -#endif /* * Disable all wakeup GPEs before entering requested sleep state. diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index 83a8d309790..edee2806e37 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -39,15 +39,12 @@ ACPI_MODULE_NAME("system"); #define ACPI_SYSTEM_CLASS "system" #define ACPI_SYSTEM_DEVICE_NAME "System" -#define ACPI_SYSTEM_FILE_INFO "info" -#define ACPI_SYSTEM_FILE_EVENT "event" -#define ACPI_SYSTEM_FILE_DSDT "dsdt" -#define ACPI_SYSTEM_FILE_FADT "fadt" /* * Make ACPICA version work as module param */ -static int param_get_acpica_version(char *buffer, struct kernel_param *kp) { +static int param_get_acpica_version(char *buffer, struct kernel_param *kp) +{ int result; result = sprintf(buffer, "%x", ACPI_CA_VERSION); @@ -58,9 +55,126 @@ static int param_get_acpica_version(char *buffer, struct kernel_param *kp) { module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444); /* -------------------------------------------------------------------------- + FS Interface (/sys) + -------------------------------------------------------------------------- */ +static LIST_HEAD(acpi_table_attr_list); +static struct kobject tables_kobj; + +struct acpi_table_attr { + struct bin_attribute attr; + char name[8]; + int instance; + struct list_head node; +}; + +static ssize_t acpi_table_show(struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t offset, size_t count) +{ + struct acpi_table_attr *table_attr = + container_of(bin_attr, struct acpi_table_attr, attr); + struct acpi_table_header *table_header = NULL; + acpi_status status; + ssize_t ret_count = count; + + status = + acpi_get_table(table_attr->name, table_attr->instance, + &table_header); + if (ACPI_FAILURE(status)) + return -ENODEV; + + if (offset >= table_header->length) { + ret_count = 0; + goto end; + } + + if (offset + ret_count > table_header->length) + ret_count = table_header->length - offset; + + memcpy(buf, ((char *)table_header) + offset, ret_count); + + end: + return ret_count; +} + +static void acpi_table_attr_init(struct acpi_table_attr *table_attr, + struct acpi_table_header *table_header) +{ + struct acpi_table_header *header = NULL; + struct acpi_table_attr *attr = NULL; + + memcpy(table_attr->name, table_header->signature, ACPI_NAME_SIZE); + + list_for_each_entry(attr, &acpi_table_attr_list, node) { + if (!memcmp(table_header->signature, attr->name, + ACPI_NAME_SIZE)) + if (table_attr->instance < attr->instance) + table_attr->instance = attr->instance; + } + table_attr->instance++; + + if (table_attr->instance > 1 || (table_attr->instance == 1 && + !acpi_get_table(table_header-> + signature, 2, + &header))) + sprintf(table_attr->name + 4, "%d", table_attr->instance); + + table_attr->attr.size = 0; + table_attr->attr.read = acpi_table_show; + table_attr->attr.attr.name = table_attr->name; + table_attr->attr.attr.mode = 0444; + table_attr->attr.attr.owner = THIS_MODULE; + + return; +} + +static int acpi_system_sysfs_init(void) +{ + struct acpi_table_attr *table_attr; + struct acpi_table_header *table_header = NULL; + int table_index = 0; + int result; + + tables_kobj.parent = &acpi_subsys.kobj; + kobject_set_name(&tables_kobj, "tables"); + result = kobject_register(&tables_kobj); + if (result) + return result; + + do { + result = acpi_get_table_by_index(table_index, &table_header); + if (!result) { + table_index++; + table_attr = NULL; + table_attr = + kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL); + if (!table_attr) + return -ENOMEM; + + acpi_table_attr_init(table_attr, table_header); + result = + sysfs_create_bin_file(&tables_kobj, + &table_attr->attr); + if (result) { + kfree(table_attr); + return result; + } else + list_add_tail(&table_attr->node, + &acpi_table_attr_list); + } + } while (!result); + + return 0; +} + +/* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ #ifdef CONFIG_ACPI_PROCFS +#define ACPI_SYSTEM_FILE_INFO "info" +#define ACPI_SYSTEM_FILE_EVENT "event" +#define ACPI_SYSTEM_FILE_DSDT "dsdt" +#define ACPI_SYSTEM_FILE_FADT "fadt" static int acpi_system_read_info(struct seq_file *seq, void *offset) { @@ -80,7 +194,6 @@ static const struct file_operations acpi_system_info_ops = { .llseek = seq_lseek, .release = single_release, }; -#endif static ssize_t acpi_system_read_dsdt(struct file *, char __user *, size_t, loff_t *); @@ -97,13 +210,11 @@ acpi_system_read_dsdt(struct file *file, struct acpi_table_header *dsdt = NULL; ssize_t res; - status = acpi_get_table(ACPI_SIG_DSDT, 1, &dsdt); if (ACPI_FAILURE(status)) return -ENODEV; - res = simple_read_from_buffer(buffer, count, ppos, - dsdt, dsdt->length); + res = simple_read_from_buffer(buffer, count, ppos, dsdt, dsdt->length); return res; } @@ -123,28 +234,21 @@ acpi_system_read_fadt(struct file *file, struct acpi_table_header *fadt = NULL; ssize_t res; - status = acpi_get_table(ACPI_SIG_FADT, 1, &fadt); if (ACPI_FAILURE(status)) return -ENODEV; - res = simple_read_from_buffer(buffer, count, ppos, - fadt, fadt->length); + res = simple_read_from_buffer(buffer, count, ppos, fadt, fadt->length); return res; } -static int __init acpi_system_init(void) +static int acpi_system_procfs_init(void) { struct proc_dir_entry *entry; int error = 0; char *name; - - if (acpi_disabled) - return 0; - -#ifdef CONFIG_ACPI_PROCFS /* 'info' [R] */ name = ACPI_SYSTEM_FILE_INFO; entry = create_proc_entry(name, S_IRUGO, acpi_root_dir); @@ -153,7 +257,6 @@ static int __init acpi_system_init(void) else { entry->proc_fops = &acpi_system_info_ops; } -#endif /* 'dsdt' [R] */ name = ACPI_SYSTEM_FILE_DSDT; @@ -177,12 +280,32 @@ static int __init acpi_system_init(void) Error: remove_proc_entry(ACPI_SYSTEM_FILE_FADT, acpi_root_dir); remove_proc_entry(ACPI_SYSTEM_FILE_DSDT, acpi_root_dir); -#ifdef CONFIG_ACPI_PROCFS remove_proc_entry(ACPI_SYSTEM_FILE_INFO, acpi_root_dir); -#endif error = -EFAULT; goto Done; } +#else +static int acpi_system_procfs_init(void) +{ + return 0; +} +#endif + +static int __init acpi_system_init(void) +{ + int result = 0; + + if (acpi_disabled) + return 0; + + result = acpi_system_procfs_init(); + if (result) + return result; + + result = acpi_system_sysfs_init(); + + return result; +} subsys_initcall(acpi_system_init); diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/tables/tbfadt.c index 1285e91474f..002bb33003a 100644 --- a/drivers/acpi/tables/tbfadt.c +++ b/drivers/acpi/tables/tbfadt.c @@ -211,14 +211,17 @@ void acpi_tb_parse_fadt(acpi_native_uint table_index, u8 flags) * DESCRIPTION: Get a local copy of the FADT and convert it to a common format. * Performs validation on some important FADT fields. * + * NOTE: We create a local copy of the FADT regardless of the version. + * ******************************************************************************/ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) { /* - * Check if the FADT is larger than what we know about (ACPI 2.0 version). - * Truncate the table, but make some noise. + * Check if the FADT is larger than the largest table that we expect + * (the ACPI 2.0/3.0 version). If so, truncate the table, and issue + * a warning. */ if (length > sizeof(struct acpi_table_fadt)) { ACPI_WARNING((AE_INFO, @@ -227,10 +230,12 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) sizeof(struct acpi_table_fadt))); } - /* Copy the entire FADT locally. Zero first for tb_convert_fadt */ + /* Clear the entire local FADT */ ACPI_MEMSET(&acpi_gbl_FADT, 0, sizeof(struct acpi_table_fadt)); + /* Copy the original FADT, up to sizeof (struct acpi_table_fadt) */ + ACPI_MEMCPY(&acpi_gbl_FADT, table, ACPI_MIN(length, sizeof(struct acpi_table_fadt))); @@ -251,7 +256,7 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) * RETURN: None * * DESCRIPTION: Converts all versions of the FADT to a common internal format. - * -> Expand all 32-bit addresses to 64-bit. + * Expand all 32-bit addresses to 64-bit. * * NOTE: acpi_gbl_FADT must be of size (struct acpi_table_fadt), * and must contain a copy of the actual FADT. @@ -292,8 +297,23 @@ static void acpi_tb_convert_fadt(void) } /* - * Expand the 32-bit V1.0 addresses to the 64-bit "X" generic address - * structures as necessary. + * For ACPI 1.0 FADTs (revision 1 or 2), ensure that reserved fields which + * should be zero are indeed zero. This will workaround BIOSs that + * inadvertently place values in these fields. + * + * The ACPI 1.0 reserved fields that will be zeroed are the bytes located at + * offset 45, 55, 95, and the word located at offset 109, 110. + */ + if (acpi_gbl_FADT.header.revision < 3) { + acpi_gbl_FADT.preferred_profile = 0; + acpi_gbl_FADT.pstate_control = 0; + acpi_gbl_FADT.cst_control = 0; + acpi_gbl_FADT.boot_flags = 0; + } + + /* + * Expand the ACPI 1.0 32-bit V1.0 addresses to the ACPI 2.0 64-bit "X" + * generic address structures as necessary. */ for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) { target = @@ -349,18 +369,6 @@ static void acpi_tb_convert_fadt(void) acpi_gbl_FADT.xpm1a_event_block.space_id; } - - /* - * For ACPI 1.0 FADTs, ensure that reserved fields (which should be zero) - * are indeed zero. This will workaround BIOSs that inadvertently placed - * values in these fields. - */ - if (acpi_gbl_FADT.header.revision < 3) { - acpi_gbl_FADT.preferred_profile = 0; - acpi_gbl_FADT.pstate_control = 0; - acpi_gbl_FADT.cst_control = 0; - acpi_gbl_FADT.boot_flags = 0; - } } /****************************************************************************** diff --git a/drivers/acpi/utilities/uteval.c b/drivers/acpi/utilities/uteval.c index 8ec6f8e4813..f112af433e3 100644 --- a/drivers/acpi/utilities/uteval.c +++ b/drivers/acpi/utilities/uteval.c @@ -62,16 +62,13 @@ acpi_ut_translate_one_cid(union acpi_operand_object *obj_desc, static char *acpi_interfaces_supported[] = { /* Operating System Vendor Strings */ - "Windows 2000", - "Windows 2001", - "Windows 2001 SP0", - "Windows 2001 SP1", - "Windows 2001 SP2", - "Windows 2001 SP3", - "Windows 2001 SP4", - "Windows 2001.1", - "Windows 2001.1 SP1", /* Added 03/2006 */ - "Windows 2006", /* Added 03/2006 */ + "Windows 2000", /* Windows 2000 */ + "Windows 2001", /* Windows XP */ + "Windows 2001 SP1", /* Windows XP SP1 */ + "Windows 2001 SP2", /* Windows XP SP2 */ + "Windows 2001.1", /* Windows Server 2003 */ + "Windows 2001.1 SP1", /* Windows Server 2003 SP1 - Added 03/2006 */ + "Windows 2006", /* Windows Vista - Added 03/2006 */ /* Feature Group Strings */ diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 00d25b34725..04ea697f72b 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -33,6 +33,7 @@ #include <linux/seq_file.h> #include <linux/backlight.h> +#include <linux/video_output.h> #include <asm/uaccess.h> #include <acpi/acpi_bus.h> @@ -169,6 +170,7 @@ struct acpi_video_device { struct acpi_device *dev; struct acpi_video_device_brightness *brightness; struct backlight_device *backlight; + struct output_device *output_dev; }; /* bus */ @@ -272,13 +274,17 @@ static int acpi_video_get_next_level(struct acpi_video_device *device, u32 level_current, u32 event); static void acpi_video_switch_brightness(struct acpi_video_device *device, int event); +static int acpi_video_device_get_state(struct acpi_video_device *device, + unsigned long *state); +static int acpi_video_output_get(struct output_device *od); +static int acpi_video_device_set_state(struct acpi_video_device *device, int state); /*backlight device sysfs support*/ static int acpi_video_get_brightness(struct backlight_device *bd) { unsigned long cur_level; struct acpi_video_device *vd = - (struct acpi_video_device *)class_get_devdata(&bd->class_dev); + (struct acpi_video_device *)bl_get_data(bd); acpi_video_device_lcd_get_level_current(vd, &cur_level); return (int) cur_level; } @@ -287,7 +293,7 @@ static int acpi_video_set_brightness(struct backlight_device *bd) { int request_level = bd->props.brightness; struct acpi_video_device *vd = - (struct acpi_video_device *)class_get_devdata(&bd->class_dev); + (struct acpi_video_device *)bl_get_data(bd); acpi_video_device_lcd_set_level(vd, request_level); return 0; } @@ -297,6 +303,28 @@ static struct backlight_ops acpi_backlight_ops = { .update_status = acpi_video_set_brightness, }; +/*video output device sysfs support*/ +static int acpi_video_output_get(struct output_device *od) +{ + unsigned long state; + struct acpi_video_device *vd = + (struct acpi_video_device *)class_get_devdata(&od->class_dev); + acpi_video_device_get_state(vd, &state); + return (int)state; +} + +static int acpi_video_output_set(struct output_device *od) +{ + unsigned long state = od->request_state; + struct acpi_video_device *vd= + (struct acpi_video_device *)class_get_devdata(&od->class_dev); + return acpi_video_device_set_state(vd, state); +} + +static struct output_properties acpi_output_properties = { + .set_state = acpi_video_output_set, + .get_status = acpi_video_output_get, +}; /* -------------------------------------------------------------------------- Video Management -------------------------------------------------------------------------- */ @@ -531,7 +559,6 @@ acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) static void acpi_video_device_find_cap(struct acpi_video_device *device) { - acpi_integer status; acpi_handle h_dummy1; int i; u32 max_level = 0; @@ -565,50 +592,55 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) device->cap._DSS = 1; } - status = acpi_video_device_lcd_query_levels(device, &obj); - - if (obj && obj->type == ACPI_TYPE_PACKAGE && obj->package.count >= 2) { - int count = 0; - union acpi_object *o; - - br = kzalloc(sizeof(*br), GFP_KERNEL); - if (!br) { - printk(KERN_ERR "can't allocate memory\n"); - } else { - br->levels = kmalloc(obj->package.count * - sizeof *(br->levels), GFP_KERNEL); - if (!br->levels) - goto out; - - for (i = 0; i < obj->package.count; i++) { - o = (union acpi_object *)&obj->package. - elements[i]; - if (o->type != ACPI_TYPE_INTEGER) { - printk(KERN_ERR PREFIX "Invalid data\n"); - continue; - } - br->levels[count] = (u32) o->integer.value; - if (br->levels[count] > max_level) - max_level = br->levels[count]; - count++; - } - out: - if (count < 2) { - kfree(br->levels); - kfree(br); + if (ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) { + + if (obj->package.count >= 2) { + int count = 0; + union acpi_object *o; + + br = kzalloc(sizeof(*br), GFP_KERNEL); + if (!br) { + printk(KERN_ERR "can't allocate memory\n"); } else { - br->count = count; - device->brightness = br; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "found %d brightness levels\n", - count)); + br->levels = kmalloc(obj->package.count * + sizeof *(br->levels), GFP_KERNEL); + if (!br->levels) + goto out; + + for (i = 0; i < obj->package.count; i++) { + o = (union acpi_object *)&obj->package. + elements[i]; + if (o->type != ACPI_TYPE_INTEGER) { + printk(KERN_ERR PREFIX "Invalid data\n"); + continue; + } + br->levels[count] = (u32) o->integer.value; + + if (br->levels[count] > max_level) + max_level = br->levels[count]; + count++; + } + out: + if (count < 2) { + kfree(br->levels); + kfree(br); + } else { + br->count = count; + device->brightness = br; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "found %d brightness levels\n", + count)); + } } } + + } else { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available LCD brightness level\n")); } kfree(obj); - if (device->cap._BCL && device->cap._BCM && device->cap._BQC){ + if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){ unsigned long tmp; static int count = 0; char *name; @@ -626,6 +658,17 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) kfree(name); } + if (device->cap._DCS && device->cap._DSS){ + static int count = 0; + char *name; + name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); + if (!name) + return; + sprintf(name, "acpi_video%d", count++); + device->output_dev = video_output_register(name, + NULL, device, &acpi_output_properties); + kfree(name); + } return; } @@ -1669,6 +1712,7 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) ACPI_DEVICE_NOTIFY, acpi_video_device_notify); backlight_device_unregister(device->backlight); + video_output_unregister(device->output_dev); return 0; } diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c index b37f1d5a5be..a08f8f981c1 100644 --- a/drivers/char/hvc_iseries.c +++ b/drivers/char/hvc_iseries.c @@ -472,7 +472,7 @@ static void hvc_handle_event(struct HvLpEvent *event) } } -static int send_open(HvLpIndex remoteLp, void *sem) +static int __init send_open(HvLpIndex remoteLp, void *sem) { return HvCallEvent_signalLpEventFast(remoteLp, HvLpEvent_Type_VirtualIo, @@ -484,7 +484,7 @@ static int send_open(HvLpIndex remoteLp, void *sem) 0, 0, 0, 0); } -static int hvc_vio_init(void) +static int __init hvc_vio_init(void) { atomic_t wait_flag; int rc; @@ -552,14 +552,14 @@ static int hvc_vio_init(void) } module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ -static void hvc_vio_exit(void) +static void __exit hvc_vio_exit(void) { vio_unregister_driver(&hvc_vio_driver); } module_exit(hvc_vio_exit); /* the device tree order defines our numbering */ -static int hvc_find_vtys(void) +static int __init hvc_find_vtys(void) { struct device_node *vty; int num_found = 0; diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c index 4b97eaf1860..bb09413d5a2 100644 --- a/drivers/char/hvc_rtas.c +++ b/drivers/char/hvc_rtas.c @@ -115,7 +115,7 @@ static void __exit hvc_rtas_exit(void) module_exit(hvc_rtas_exit); /* This will happen prior to module init. There is no tty at this time? */ -static int hvc_rtas_console_init(void) +static int __init hvc_rtas_console_init(void) { rtascons_put_char_token = rtas_token("put-term-char"); if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE) diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c index 17f96e04266..69d8866de78 100644 --- a/drivers/char/hvcs.c +++ b/drivers/char/hvcs.c @@ -210,9 +210,9 @@ static struct ktermios hvcs_tty_termios = { static int hvcs_parm_num_devs = -1; module_param(hvcs_parm_num_devs, int, 0); -char hvcs_driver_name[] = "hvcs"; -char hvcs_device_node[] = "hvcs"; -char hvcs_driver_string[] +static const char hvcs_driver_name[] = "hvcs"; +static const char hvcs_device_node[] = "hvcs"; +static const char hvcs_driver_string[] = "IBM hvcs (Hypervisor Virtual Console Server) Driver"; /* Status of partner info rescan triggered via sysfs. */ @@ -1092,7 +1092,7 @@ static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, * NOTICE: Do NOT hold either the hvcs_struct.lock or hvcs_structs_lock when * calling this function or you will get deadlock. */ -struct hvcs_struct *hvcs_get_by_index(int index) +static struct hvcs_struct *hvcs_get_by_index(int index) { struct hvcs_struct *hvcsd = NULL; unsigned long flags; diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index ad5cc5f6862..16fb23125e9 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -187,6 +187,22 @@ config PNX4008_WATCHDOG Say N if you are unsure. +config IOP_WATCHDOG + tristate "IOP Watchdog" + depends on WATCHDOG && PLAT_IOP + select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X) + help + Say Y here if to include support for the watchdog timer + in the Intel IOP3XX & IOP13XX I/O Processors. This driver can + be built as a module by choosing M. The module will + be called iop_wdt. + + Note: The IOP13XX watchdog does an Internal Bus Reset which will + affect both cores and the peripherals of the IOP. The ATU-X + and/or ATUe configuration registers will remain intact, but if + operating as an Root Complex and/or Central Resource, the PCI-X + and/or PCIe busses will also be reset. THIS IS A VERY BIG HAMMER. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index 3907ec04a4e..bdb9d5e3bb4 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o +obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/char/watchdog/iop_wdt.c b/drivers/char/watchdog/iop_wdt.c new file mode 100644 index 00000000000..bbbd91af754 --- /dev/null +++ b/drivers/char/watchdog/iop_wdt.c @@ -0,0 +1,262 @@ +/* + * drivers/char/watchdog/iop_wdt.c + * + * WDT driver for Intel I/O Processors + * Copyright (C) 2005, Intel Corporation. + * + * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Curt E Bruns <curt.e.bruns@intel.com> + * Peter Milne <peter.milne@d-tacq.com> + * Dan Williams <dan.j.williams@intel.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/uaccess.h> +#include <asm/hardware.h> + +static int nowayout = WATCHDOG_NOWAYOUT; +static unsigned long wdt_status; +static unsigned long boot_status; + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 +#define WDT_ENABLED 2 + +static unsigned long iop_watchdog_timeout(void) +{ + return (0xffffffffUL / get_iop_tick_rate()); +} + +/** + * wdt_supports_disable - determine if we are accessing a iop13xx watchdog + * or iop3xx by whether it has a disable command + */ +static int wdt_supports_disable(void) +{ + int can_disable; + + if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM) + can_disable = 1; + else + can_disable = 0; + + return can_disable; +} + +static void wdt_enable(void) +{ + /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF + * Takes approx. 10.7s to timeout + */ + write_wdtcr(IOP_WDTCR_EN_ARM); + write_wdtcr(IOP_WDTCR_EN); +} + +/* returns 0 if the timer was successfully disabled */ +static int wdt_disable(void) +{ + /* Stop Counting */ + if (wdt_supports_disable()) { + write_wdtcr(IOP_WDTCR_DIS_ARM); + write_wdtcr(IOP_WDTCR_DIS); + clear_bit(WDT_ENABLED, &wdt_status); + printk(KERN_INFO "WATCHDOG: Disabled\n"); + return 0; + } else + return 1; +} + +static int iop_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + wdt_enable(); + + set_bit(WDT_ENABLED, &wdt_status); + + return nonseekable_open(inode, file); +} + +static ssize_t +iop_wdt_write(struct file *file, const char *data, size_t len, + loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_enable(); + } + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, + .identity = "iop watchdog", +}; + +static int +iop_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int options; + int ret = -ENOTTY; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user + ((struct watchdog_info *)arg, &ident, sizeof ident)) + ret = -EFAULT; + else + ret = 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, (int *)arg); + break; + + case WDIOC_GETTIMEOUT: + ret = put_user(iop_watchdog_timeout(), (int *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_enable(); + ret = 0; + break; + + case WDIOC_SETOPTIONS: + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + if (!nowayout) { + if (wdt_disable() == 0) { + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + ret = 0; + } else + ret = -ENXIO; + } else + ret = 0; + } + + if (options & WDIOS_ENABLECARD) { + wdt_enable(); + ret = 0; + } + break; + } + + return ret; +} + +static int iop_wdt_release(struct inode *inode, struct file *file) +{ + int state = 1; + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) + if (test_bit(WDT_ENABLED, &wdt_status)) + state = wdt_disable(); + + /* if the timer is not disbaled reload and notify that we are still + * going down + */ + if (state != 0) { + wdt_enable(); + printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " + "reset in %lu seconds\n", iop_watchdog_timeout()); + } + + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + +static const struct file_operations iop_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = iop_wdt_write, + .ioctl = iop_wdt_ioctl, + .open = iop_wdt_open, + .release = iop_wdt_release, +}; + +static struct miscdevice iop_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &iop_wdt_fops, +}; + +static int __init iop_wdt_init(void) +{ + int ret; + + ret = misc_register(&iop_wdt_miscdev); + if (ret == 0) + printk("iop watchdog timer: timeout %lu sec\n", + iop_watchdog_timeout()); + + /* check if the reset was caused by the watchdog timer */ + boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0; + + /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset + * NOTE: An IB Reset will Reset both cores in the IOP342 + */ + write_wdtsr(IOP13XX_WDTCR_IB_RESET); + + return ret; +} + +static void __exit iop_wdt_exit(void) +{ + misc_deregister(&iop_wdt_miscdev); +} + +module_init(iop_wdt_init); +module_exit(iop_wdt_exit); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>"); +MODULE_DESCRIPTION("iop watchdog timer driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index fc984474162..3e4a369d005 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -1160,7 +1160,7 @@ static struct device_attribute *sbp2_scsi_sysfs_attrs[] = { static struct scsi_host_template scsi_driver_template = { .module = THIS_MODULE, .name = "SBP-2 IEEE-1394", - .proc_name = (char *)sbp2_driver_name, + .proc_name = sbp2_driver_name, .queuecommand = sbp2_scsi_queuecommand, .slave_alloc = sbp2_scsi_slave_alloc, .slave_configure = sbp2_scsi_slave_configure, diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h index 043e4fb23fb..3725aa8664d 100644 --- a/drivers/infiniband/hw/ehca/ehca_classes.h +++ b/drivers/infiniband/hw/ehca/ehca_classes.h @@ -43,7 +43,6 @@ #ifndef __EHCA_CLASSES_H__ #define __EHCA_CLASSES_H__ - struct ehca_module; struct ehca_qp; struct ehca_cq; @@ -100,6 +99,11 @@ struct ehca_sport { struct ehca_sma_attr saved_attr; }; +#define HCA_CAP_MR_PGSIZE_4K 1 +#define HCA_CAP_MR_PGSIZE_64K 2 +#define HCA_CAP_MR_PGSIZE_1M 4 +#define HCA_CAP_MR_PGSIZE_16M 8 + struct ehca_shca { struct ib_device ib_device; struct ibmebus_dev *ibmebus_dev; @@ -115,6 +119,8 @@ struct ehca_shca { struct h_galpas galpas; struct mutex modify_mutex; u64 hca_cap; + /* MR pgsize: bit 0-3 means 4K, 64K, 1M, 16M respectively */ + u32 hca_cap_mr_pgsize; int max_mtu; }; @@ -122,6 +128,10 @@ struct ehca_pd { struct ib_pd ib_pd; struct ipz_pd fw_pd; u32 ownpid; + /* small queue mgmt */ + struct mutex lock; + struct list_head free[2]; + struct list_head full[2]; }; enum ehca_ext_qp_type { @@ -206,6 +216,7 @@ struct ehca_mr { enum ehca_mr_flag flags; u32 num_kpages; /* number of kernel pages */ u32 num_hwpages; /* number of hw pages to form MR */ + u64 hwpage_size; /* hw page size used for this MR */ int acl; /* ACL (stored here for usage in reregister) */ u64 *start; /* virtual start address (stored here for */ /* usage in reregister) */ @@ -240,6 +251,7 @@ struct ehca_mr_pginfo { enum ehca_mr_pgi_type type; u64 num_kpages; u64 kpage_cnt; + u64 hwpage_size; /* hw page size used for this MR */ u64 num_hwpages; /* number of hw pages */ u64 hwpage_cnt; /* counter for hw pages */ u64 next_hwpage; /* next hw page in buffer/chunk/listelem */ @@ -298,6 +310,8 @@ int ehca_init_av_cache(void); void ehca_cleanup_av_cache(void); int ehca_init_mrmw_cache(void); void ehca_cleanup_mrmw_cache(void); +int ehca_init_small_qp_cache(void); +void ehca_cleanup_small_qp_cache(void); extern rwlock_t ehca_qp_idr_lock; extern rwlock_t ehca_cq_idr_lock; @@ -315,7 +329,7 @@ struct ipzu_queue_resp { u32 queue_length; /* queue length allocated in bytes */ u32 pagesize; u32 toggle_state; - u32 dummy; /* padding for 8 byte alignment */ + u32 offset; /* save offset within a page for small_qp */ }; struct ehca_create_cq_resp { @@ -357,15 +371,29 @@ enum ehca_ll_comp_flags { LLQP_COMP_MASK = 0x60, }; +struct ehca_alloc_queue_parms { + /* input parameters */ + int max_wr; + int max_sge; + int page_size; + int is_small; + + /* output parameters */ + u16 act_nr_wqes; + u8 act_nr_sges; + u32 queue_size; /* bytes for small queues, pages otherwise */ +}; + struct ehca_alloc_qp_parms { -/* input parameters */ + struct ehca_alloc_queue_parms squeue; + struct ehca_alloc_queue_parms rqueue; + + /* input parameters */ enum ehca_service_type servicetype; + int qp_storage; int sigtype; enum ehca_ext_qp_type ext_type; enum ehca_ll_comp_flags ll_comp_flags; - - int max_send_wr, max_recv_wr; - int max_send_sge, max_recv_sge; int ud_av_l_key_ctl; u32 token; @@ -375,18 +403,10 @@ struct ehca_alloc_qp_parms { u32 srq_qpn, srq_token, srq_limit; -/* output parameters */ + /* output parameters */ u32 real_qp_num; struct ipz_qp_handle qp_handle; struct h_galpas galpas; - - u16 act_nr_send_wqes; - u16 act_nr_recv_wqes; - u8 act_nr_recv_sges; - u8 act_nr_send_sges; - - u32 nr_rq_pages; - u32 nr_sq_pages; }; int ehca_cq_assign_qp(struct ehca_cq *cq, struct ehca_qp *qp); diff --git a/drivers/infiniband/hw/ehca/ehca_cq.c b/drivers/infiniband/hw/ehca/ehca_cq.c index 1e8ca3fca4a..81aff36101b 100644 --- a/drivers/infiniband/hw/ehca/ehca_cq.c +++ b/drivers/infiniband/hw/ehca/ehca_cq.c @@ -190,8 +190,8 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector, goto create_cq_exit2; } - ipz_rc = ipz_queue_ctor(&my_cq->ipz_queue, param.act_pages, - EHCA_PAGESIZE, sizeof(struct ehca_cqe), 0); + ipz_rc = ipz_queue_ctor(NULL, &my_cq->ipz_queue, param.act_pages, + EHCA_PAGESIZE, sizeof(struct ehca_cqe), 0, 0); if (!ipz_rc) { ehca_err(device, "ipz_queue_ctor() failed ipz_rc=%x device=%p", ipz_rc, device); @@ -285,7 +285,7 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector, return cq; create_cq_exit4: - ipz_queue_dtor(&my_cq->ipz_queue); + ipz_queue_dtor(NULL, &my_cq->ipz_queue); create_cq_exit3: h_ret = hipz_h_destroy_cq(adapter_handle, my_cq, 1); @@ -359,7 +359,7 @@ int ehca_destroy_cq(struct ib_cq *cq) "ehca_cq=%p cq_num=%x", h_ret, my_cq, cq_num); return ehca2ib_return_code(h_ret); } - ipz_queue_dtor(&my_cq->ipz_queue); + ipz_queue_dtor(NULL, &my_cq->ipz_queue); kmem_cache_free(cq_cache, my_cq); return 0; diff --git a/drivers/infiniband/hw/ehca/ehca_eq.c b/drivers/infiniband/hw/ehca/ehca_eq.c index 4825975f88c..1d41faa7a33 100644 --- a/drivers/infiniband/hw/ehca/ehca_eq.c +++ b/drivers/infiniband/hw/ehca/ehca_eq.c @@ -86,8 +86,8 @@ int ehca_create_eq(struct ehca_shca *shca, return -EINVAL; } - ret = ipz_queue_ctor(&eq->ipz_queue, nr_pages, - EHCA_PAGESIZE, sizeof(struct ehca_eqe), 0); + ret = ipz_queue_ctor(NULL, &eq->ipz_queue, nr_pages, + EHCA_PAGESIZE, sizeof(struct ehca_eqe), 0, 0); if (!ret) { ehca_err(ib_dev, "Can't allocate EQ pages eq=%p", eq); goto create_eq_exit1; @@ -145,7 +145,7 @@ int ehca_create_eq(struct ehca_shca *shca, return 0; create_eq_exit2: - ipz_queue_dtor(&eq->ipz_queue); + ipz_queue_dtor(NULL, &eq->ipz_queue); create_eq_exit1: hipz_h_destroy_eq(shca->ipz_hca_handle, eq); @@ -181,7 +181,7 @@ int ehca_destroy_eq(struct ehca_shca *shca, struct ehca_eq *eq) ehca_err(&shca->ib_device, "Can't free EQ resources."); return -EINVAL; } - ipz_queue_dtor(&eq->ipz_queue); + ipz_queue_dtor(NULL, &eq->ipz_queue); return 0; } diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c index 4fb01fcb63a..71c0799b350 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.c +++ b/drivers/infiniband/hw/ehca/ehca_irq.c @@ -175,9 +175,8 @@ error_data1: } -static void qp_event_callback(struct ehca_shca *shca, - u64 eqe, - enum ib_event_type event_type) +static void qp_event_callback(struct ehca_shca *shca, u64 eqe, + enum ib_event_type event_type, int fatal) { struct ib_event event; struct ehca_qp *qp; @@ -191,16 +190,26 @@ static void qp_event_callback(struct ehca_shca *shca, if (!qp) return; - ehca_error_data(shca, qp, qp->ipz_qp_handle.handle); + if (fatal) + ehca_error_data(shca, qp, qp->ipz_qp_handle.handle); - if (!qp->ib_qp.event_handler) - return; + event.device = &shca->ib_device; - event.device = &shca->ib_device; - event.event = event_type; - event.element.qp = &qp->ib_qp; + if (qp->ext_type == EQPT_SRQ) { + if (!qp->ib_srq.event_handler) + return; - qp->ib_qp.event_handler(&event, qp->ib_qp.qp_context); + event.event = fatal ? IB_EVENT_SRQ_ERR : event_type; + event.element.srq = &qp->ib_srq; + qp->ib_srq.event_handler(&event, qp->ib_srq.srq_context); + } else { + if (!qp->ib_qp.event_handler) + return; + + event.event = event_type; + event.element.qp = &qp->ib_qp; + qp->ib_qp.event_handler(&event, qp->ib_qp.qp_context); + } return; } @@ -234,17 +243,17 @@ static void parse_identifier(struct ehca_shca *shca, u64 eqe) switch (identifier) { case 0x02: /* path migrated */ - qp_event_callback(shca, eqe, IB_EVENT_PATH_MIG); + qp_event_callback(shca, eqe, IB_EVENT_PATH_MIG, 0); break; case 0x03: /* communication established */ - qp_event_callback(shca, eqe, IB_EVENT_COMM_EST); + qp_event_callback(shca, eqe, IB_EVENT_COMM_EST, 0); break; case 0x04: /* send queue drained */ - qp_event_callback(shca, eqe, IB_EVENT_SQ_DRAINED); + qp_event_callback(shca, eqe, IB_EVENT_SQ_DRAINED, 0); break; case 0x05: /* QP error */ case 0x06: /* QP error */ - qp_event_callback(shca, eqe, IB_EVENT_QP_FATAL); + qp_event_callback(shca, eqe, IB_EVENT_QP_FATAL, 1); break; case 0x07: /* CQ error */ case 0x08: /* CQ error */ @@ -278,6 +287,11 @@ static void parse_identifier(struct ehca_shca *shca, u64 eqe) ehca_err(&shca->ib_device, "Interface trace stopped."); break; case 0x14: /* first error capture info available */ + ehca_info(&shca->ib_device, "First error capture available"); + break; + case 0x15: /* SRQ limit reached */ + qp_event_callback(shca, eqe, IB_EVENT_SRQ_LIMIT_REACHED, 0); + break; default: ehca_err(&shca->ib_device, "Unknown identifier: %x on %s.", identifier, shca->ib_device.name); diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c index 04c324330b7..99036b65bb8 100644 --- a/drivers/infiniband/hw/ehca/ehca_main.c +++ b/drivers/infiniband/hw/ehca/ehca_main.c @@ -63,6 +63,7 @@ int ehca_port_act_time = 30; int ehca_poll_all_eqs = 1; int ehca_static_rate = -1; int ehca_scaling_code = 0; +int ehca_mr_largepage = 0; module_param_named(open_aqp1, ehca_open_aqp1, int, 0); module_param_named(debug_level, ehca_debug_level, int, 0); @@ -72,7 +73,8 @@ module_param_named(use_hp_mr, ehca_use_hp_mr, int, 0); module_param_named(port_act_time, ehca_port_act_time, int, 0); module_param_named(poll_all_eqs, ehca_poll_all_eqs, int, 0); module_param_named(static_rate, ehca_static_rate, int, 0); -module_param_named(scaling_code, ehca_scaling_code, int, 0); +module_param_named(scaling_code, ehca_scaling_code, int, 0); +module_param_named(mr_largepage, ehca_mr_largepage, int, 0); MODULE_PARM_DESC(open_aqp1, "AQP1 on startup (0: no (default), 1: yes)"); @@ -95,6 +97,9 @@ MODULE_PARM_DESC(static_rate, "set permanent static rate (default: disabled)"); MODULE_PARM_DESC(scaling_code, "set scaling code (0: disabled/default, 1: enabled)"); +MODULE_PARM_DESC(mr_largepage, + "use large page for MR (0: use PAGE_SIZE (default), " + "1: use large page depending on MR size"); DEFINE_RWLOCK(ehca_qp_idr_lock); DEFINE_RWLOCK(ehca_cq_idr_lock); @@ -125,6 +130,23 @@ void ehca_free_fw_ctrlblock(void *ptr) } #endif +int ehca2ib_return_code(u64 ehca_rc) +{ + switch (ehca_rc) { + case H_SUCCESS: + return 0; + case H_RESOURCE: /* Resource in use */ + case H_BUSY: + return -EBUSY; + case H_NOT_ENOUGH_RESOURCES: /* insufficient resources */ + case H_CONSTRAINED: /* resource constraint */ + case H_NO_MEM: + return -ENOMEM; + default: + return -EINVAL; + } +} + static int ehca_create_slab_caches(void) { int ret; @@ -159,6 +181,12 @@ static int ehca_create_slab_caches(void) goto create_slab_caches5; } + ret = ehca_init_small_qp_cache(); + if (ret) { + ehca_gen_err("Cannot create small queue SLAB cache."); + goto create_slab_caches6; + } + #ifdef CONFIG_PPC_64K_PAGES ctblk_cache = kmem_cache_create("ehca_cache_ctblk", EHCA_PAGESIZE, H_CB_ALIGNMENT, @@ -166,12 +194,15 @@ static int ehca_create_slab_caches(void) NULL); if (!ctblk_cache) { ehca_gen_err("Cannot create ctblk SLAB cache."); - ehca_cleanup_mrmw_cache(); - goto create_slab_caches5; + ehca_cleanup_small_qp_cache(); + goto create_slab_caches6; } #endif return 0; +create_slab_caches6: + ehca_cleanup_mrmw_cache(); + create_slab_caches5: ehca_cleanup_av_cache(); @@ -189,6 +220,7 @@ create_slab_caches2: static void ehca_destroy_slab_caches(void) { + ehca_cleanup_small_qp_cache(); ehca_cleanup_mrmw_cache(); ehca_cleanup_av_cache(); ehca_cleanup_qp_cache(); @@ -295,6 +327,8 @@ int ehca_sense_attributes(struct ehca_shca *shca) if (EHCA_BMASK_GET(hca_cap_descr[i].mask, shca->hca_cap)) ehca_gen_dbg(" %s", hca_cap_descr[i].descr); + shca->hca_cap_mr_pgsize = rblock->memory_page_size_supported; + port = (struct hipz_query_port *)rblock; h_ret = hipz_h_query_port(shca->ipz_hca_handle, 1, port); if (h_ret != H_SUCCESS) { @@ -590,6 +624,14 @@ static ssize_t ehca_show_adapter_handle(struct device *dev, } static DEVICE_ATTR(adapter_handle, S_IRUGO, ehca_show_adapter_handle, NULL); +static ssize_t ehca_show_mr_largepage(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", ehca_mr_largepage); +} +static DEVICE_ATTR(mr_largepage, S_IRUGO, ehca_show_mr_largepage, NULL); + static struct attribute *ehca_dev_attrs[] = { &dev_attr_adapter_handle.attr, &dev_attr_num_ports.attr, @@ -606,6 +648,7 @@ static struct attribute *ehca_dev_attrs[] = { &dev_attr_cur_mw.attr, &dev_attr_max_pd.attr, &dev_attr_max_ah.attr, + &dev_attr_mr_largepage.attr, NULL }; diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.c b/drivers/infiniband/hw/ehca/ehca_mrmw.c index 9f4c9d46e8e..c1b868b79d6 100644 --- a/drivers/infiniband/hw/ehca/ehca_mrmw.c +++ b/drivers/infiniband/hw/ehca/ehca_mrmw.c @@ -5,6 +5,7 @@ * * Authors: Dietmar Decker <ddecker@de.ibm.com> * Christoph Raisch <raisch@de.ibm.com> + * Hoang-Nam Nguyen <hnguyen@de.ibm.com> * * Copyright (c) 2005 IBM Corporation * @@ -56,6 +57,37 @@ static struct kmem_cache *mr_cache; static struct kmem_cache *mw_cache; +enum ehca_mr_pgsize { + EHCA_MR_PGSIZE4K = 0x1000L, + EHCA_MR_PGSIZE64K = 0x10000L, + EHCA_MR_PGSIZE1M = 0x100000L, + EHCA_MR_PGSIZE16M = 0x1000000L +}; + +extern int ehca_mr_largepage; + +static u32 ehca_encode_hwpage_size(u32 pgsize) +{ + u32 idx = 0; + pgsize >>= 12; + /* + * map mr page size into hw code: + * 0, 1, 2, 3 for 4K, 64K, 1M, 64M + */ + while (!(pgsize & 1)) { + idx++; + pgsize >>= 4; + } + return idx; +} + +static u64 ehca_get_max_hwpage_size(struct ehca_shca *shca) +{ + if (shca->hca_cap_mr_pgsize & HCA_CAP_MR_PGSIZE_16M) + return EHCA_MR_PGSIZE16M; + return EHCA_MR_PGSIZE4K; +} + static struct ehca_mr *ehca_mr_new(void) { struct ehca_mr *me; @@ -207,19 +239,23 @@ struct ib_mr *ehca_reg_phys_mr(struct ib_pd *pd, struct ehca_mr_pginfo pginfo; u32 num_kpages; u32 num_hwpages; + u64 hw_pgsize; num_kpages = NUM_CHUNKS(((u64)iova_start % PAGE_SIZE) + size, PAGE_SIZE); - num_hwpages = NUM_CHUNKS(((u64)iova_start % EHCA_PAGESIZE) + - size, EHCA_PAGESIZE); + /* for kernel space we try most possible pgsize */ + hw_pgsize = ehca_get_max_hwpage_size(shca); + num_hwpages = NUM_CHUNKS(((u64)iova_start % hw_pgsize) + size, + hw_pgsize); memset(&pginfo, 0, sizeof(pginfo)); pginfo.type = EHCA_MR_PGI_PHYS; pginfo.num_kpages = num_kpages; + pginfo.hwpage_size = hw_pgsize; pginfo.num_hwpages = num_hwpages; pginfo.u.phy.num_phys_buf = num_phys_buf; pginfo.u.phy.phys_buf_array = phys_buf_array; - pginfo.next_hwpage = (((u64)iova_start & ~PAGE_MASK) / - EHCA_PAGESIZE); + pginfo.next_hwpage = + ((u64)iova_start & ~(hw_pgsize - 1)) / hw_pgsize; ret = ehca_reg_mr(shca, e_mr, iova_start, size, mr_access_flags, e_pd, &pginfo, &e_mr->ib.ib_mr.lkey, @@ -259,6 +295,7 @@ struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, int ret; u32 num_kpages; u32 num_hwpages; + u64 hwpage_size; if (!pd) { ehca_gen_err("bad pd=%p", pd); @@ -309,16 +346,32 @@ struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, /* determine number of MR pages */ num_kpages = NUM_CHUNKS((virt % PAGE_SIZE) + length, PAGE_SIZE); - num_hwpages = NUM_CHUNKS((virt % EHCA_PAGESIZE) + length, - EHCA_PAGESIZE); + /* select proper hw_pgsize */ + if (ehca_mr_largepage && + (shca->hca_cap_mr_pgsize & HCA_CAP_MR_PGSIZE_16M)) { + if (length <= EHCA_MR_PGSIZE4K + && PAGE_SIZE == EHCA_MR_PGSIZE4K) + hwpage_size = EHCA_MR_PGSIZE4K; + else if (length <= EHCA_MR_PGSIZE64K) + hwpage_size = EHCA_MR_PGSIZE64K; + else if (length <= EHCA_MR_PGSIZE1M) + hwpage_size = EHCA_MR_PGSIZE1M; + else + hwpage_size = EHCA_MR_PGSIZE16M; + } else + hwpage_size = EHCA_MR_PGSIZE4K; + ehca_dbg(pd->device, "hwpage_size=%lx", hwpage_size); +reg_user_mr_fallback: + num_hwpages = NUM_CHUNKS((virt % hwpage_size) + length, hwpage_size); /* register MR on HCA */ memset(&pginfo, 0, sizeof(pginfo)); pginfo.type = EHCA_MR_PGI_USER; + pginfo.hwpage_size = hwpage_size; pginfo.num_kpages = num_kpages; pginfo.num_hwpages = num_hwpages; pginfo.u.usr.region = e_mr->umem; - pginfo.next_hwpage = e_mr->umem->offset / EHCA_PAGESIZE; + pginfo.next_hwpage = e_mr->umem->offset / hwpage_size; pginfo.u.usr.next_chunk = list_prepare_entry(pginfo.u.usr.next_chunk, (&e_mr->umem->chunk_list), list); @@ -326,6 +379,18 @@ struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, ret = ehca_reg_mr(shca, e_mr, (u64 *)virt, length, mr_access_flags, e_pd, &pginfo, &e_mr->ib.ib_mr.lkey, &e_mr->ib.ib_mr.rkey); + if (ret == -EINVAL && pginfo.hwpage_size > PAGE_SIZE) { + ehca_warn(pd->device, "failed to register mr " + "with hwpage_size=%lx", hwpage_size); + ehca_info(pd->device, "try to register mr with " + "kpage_size=%lx", PAGE_SIZE); + /* + * this means kpages are not contiguous for a hw page + * try kernel page size as fallback solution + */ + hwpage_size = PAGE_SIZE; + goto reg_user_mr_fallback; + } if (ret) { ib_mr = ERR_PTR(ret); goto reg_user_mr_exit2; @@ -452,6 +517,8 @@ int ehca_rereg_phys_mr(struct ib_mr *mr, new_pd = container_of(mr->pd, struct ehca_pd, ib_pd); if (mr_rereg_mask & IB_MR_REREG_TRANS) { + u64 hw_pgsize = ehca_get_max_hwpage_size(shca); + new_start = iova_start; /* change address */ /* check physical buffer list and calculate size */ ret = ehca_mr_chk_buf_and_calc_size(phys_buf_array, @@ -468,16 +535,17 @@ int ehca_rereg_phys_mr(struct ib_mr *mr, } num_kpages = NUM_CHUNKS(((u64)new_start % PAGE_SIZE) + new_size, PAGE_SIZE); - num_hwpages = NUM_CHUNKS(((u64)new_start % EHCA_PAGESIZE) + - new_size, EHCA_PAGESIZE); + num_hwpages = NUM_CHUNKS(((u64)new_start % hw_pgsize) + + new_size, hw_pgsize); memset(&pginfo, 0, sizeof(pginfo)); pginfo.type = EHCA_MR_PGI_PHYS; pginfo.num_kpages = num_kpages; + pginfo.hwpage_size = hw_pgsize; pginfo.num_hwpages = num_hwpages; pginfo.u.phy.num_phys_buf = num_phys_buf; pginfo.u.phy.phys_buf_array = phys_buf_array; - pginfo.next_hwpage = (((u64)iova_start & ~PAGE_MASK) / - EHCA_PAGESIZE); + pginfo.next_hwpage = + ((u64)iova_start & ~(hw_pgsize - 1)) / hw_pgsize; } if (mr_rereg_mask & IB_MR_REREG_ACCESS) new_acl = mr_access_flags; @@ -709,6 +777,7 @@ struct ib_fmr *ehca_alloc_fmr(struct ib_pd *pd, int ret; u32 tmp_lkey, tmp_rkey; struct ehca_mr_pginfo pginfo; + u64 hw_pgsize; /* check other parameters */ if (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) && @@ -738,8 +807,8 @@ struct ib_fmr *ehca_alloc_fmr(struct ib_pd *pd, ib_fmr = ERR_PTR(-EINVAL); goto alloc_fmr_exit0; } - if (((1 << fmr_attr->page_shift) != EHCA_PAGESIZE) && - ((1 << fmr_attr->page_shift) != PAGE_SIZE)) { + hw_pgsize = ehca_get_max_hwpage_size(shca); + if ((1 << fmr_attr->page_shift) != hw_pgsize) { ehca_err(pd->device, "unsupported fmr_attr->page_shift=%x", fmr_attr->page_shift); ib_fmr = ERR_PTR(-EINVAL); @@ -755,6 +824,10 @@ struct ib_fmr *ehca_alloc_fmr(struct ib_pd *pd, /* register MR on HCA */ memset(&pginfo, 0, sizeof(pginfo)); + /* + * pginfo.num_hwpages==0, ie register_rpages() will not be called + * but deferred to map_phys_fmr() + */ ret = ehca_reg_mr(shca, e_fmr, NULL, fmr_attr->max_pages * (1 << fmr_attr->page_shift), mr_access_flags, e_pd, &pginfo, @@ -765,6 +838,7 @@ struct ib_fmr *ehca_alloc_fmr(struct ib_pd *pd, } /* successful */ + e_fmr->hwpage_size = hw_pgsize; e_fmr->fmr_page_size = 1 << fmr_attr->page_shift; e_fmr->fmr_max_pages = fmr_attr->max_pages; e_fmr->fmr_max_maps = fmr_attr->max_maps; @@ -822,10 +896,12 @@ int ehca_map_phys_fmr(struct ib_fmr *fmr, memset(&pginfo, 0, sizeof(pginfo)); pginfo.type = EHCA_MR_PGI_FMR; pginfo.num_kpages = list_len; - pginfo.num_hwpages = list_len * (e_fmr->fmr_page_size / EHCA_PAGESIZE); + pginfo.hwpage_size = e_fmr->hwpage_size; + pginfo.num_hwpages = + list_len * e_fmr->fmr_page_size / pginfo.hwpage_size; pginfo.u.fmr.page_list = page_list; - pginfo.next_hwpage = ((iova & (e_fmr->fmr_page_size-1)) / - EHCA_PAGESIZE); + pginfo.next_hwpage = + (iova & (e_fmr->fmr_page_size-1)) / pginfo.hwpage_size; pginfo.u.fmr.fmr_pgsize = e_fmr->fmr_page_size; ret = ehca_rereg_mr(shca, e_fmr, (u64 *)iova, @@ -964,7 +1040,7 @@ int ehca_reg_mr(struct ehca_shca *shca, struct ehca_mr_hipzout_parms hipzout; ehca_mrmw_map_acl(acl, &hipz_acl); - ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl); + ehca_mrmw_set_pgsize_hipz_acl(pginfo->hwpage_size, &hipz_acl); if (ehca_use_hp_mr == 1) hipz_acl |= 0x00000001; @@ -987,6 +1063,7 @@ int ehca_reg_mr(struct ehca_shca *shca, /* successful registration */ e_mr->num_kpages = pginfo->num_kpages; e_mr->num_hwpages = pginfo->num_hwpages; + e_mr->hwpage_size = pginfo->hwpage_size; e_mr->start = iova_start; e_mr->size = size; e_mr->acl = acl; @@ -1029,6 +1106,9 @@ int ehca_reg_mr_rpages(struct ehca_shca *shca, u32 i; u64 *kpage; + if (!pginfo->num_hwpages) /* in case of fmr */ + return 0; + kpage = ehca_alloc_fw_ctrlblock(GFP_KERNEL); if (!kpage) { ehca_err(&shca->ib_device, "kpage alloc failed"); @@ -1036,7 +1116,7 @@ int ehca_reg_mr_rpages(struct ehca_shca *shca, goto ehca_reg_mr_rpages_exit0; } - /* max 512 pages per shot */ + /* max MAX_RPAGES ehca mr pages per register call */ for (i = 0; i < NUM_CHUNKS(pginfo->num_hwpages, MAX_RPAGES); i++) { if (i == NUM_CHUNKS(pginfo->num_hwpages, MAX_RPAGES) - 1) { @@ -1049,8 +1129,8 @@ int ehca_reg_mr_rpages(struct ehca_shca *shca, ret = ehca_set_pagebuf(pginfo, rnum, kpage); if (ret) { ehca_err(&shca->ib_device, "ehca_set_pagebuf " - "bad rc, ret=%x rnum=%x kpage=%p", - ret, rnum, kpage); + "bad rc, ret=%x rnum=%x kpage=%p", + ret, rnum, kpage); goto ehca_reg_mr_rpages_exit1; } @@ -1065,9 +1145,10 @@ int ehca_reg_mr_rpages(struct ehca_shca *shca, } else rpage = *kpage; - h_ret = hipz_h_register_rpage_mr(shca->ipz_hca_handle, e_mr, - 0, /* pagesize 4k */ - 0, rpage, rnum); + h_ret = hipz_h_register_rpage_mr( + shca->ipz_hca_handle, e_mr, + ehca_encode_hwpage_size(pginfo->hwpage_size), + 0, rpage, rnum); if (i == NUM_CHUNKS(pginfo->num_hwpages, MAX_RPAGES) - 1) { /* @@ -1131,7 +1212,7 @@ inline int ehca_rereg_mr_rereg1(struct ehca_shca *shca, struct ehca_mr_hipzout_parms hipzout; ehca_mrmw_map_acl(acl, &hipz_acl); - ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl); + ehca_mrmw_set_pgsize_hipz_acl(pginfo->hwpage_size, &hipz_acl); kpage = ehca_alloc_fw_ctrlblock(GFP_KERNEL); if (!kpage) { @@ -1182,6 +1263,7 @@ inline int ehca_rereg_mr_rereg1(struct ehca_shca *shca, */ e_mr->num_kpages = pginfo->num_kpages; e_mr->num_hwpages = pginfo->num_hwpages; + e_mr->hwpage_size = pginfo->hwpage_size; e_mr->start = iova_start; e_mr->size = size; e_mr->acl = acl; @@ -1268,13 +1350,14 @@ int ehca_rereg_mr(struct ehca_shca *shca, /* set some MR values */ e_mr->flags = save_mr.flags; + e_mr->hwpage_size = save_mr.hwpage_size; e_mr->fmr_page_size = save_mr.fmr_page_size; e_mr->fmr_max_pages = save_mr.fmr_max_pages; e_mr->fmr_max_maps = save_mr.fmr_max_maps; e_mr->fmr_map_cnt = save_mr.fmr_map_cnt; ret = ehca_reg_mr(shca, e_mr, iova_start, size, acl, - e_pd, pginfo, lkey, rkey); + e_pd, pginfo, lkey, rkey); if (ret) { u32 offset = (u64)(&e_mr->flags) - (u64)e_mr; memcpy(&e_mr->flags, &(save_mr.flags), @@ -1355,6 +1438,7 @@ int ehca_unmap_one_fmr(struct ehca_shca *shca, /* set some MR values */ e_fmr->flags = save_fmr.flags; + e_fmr->hwpage_size = save_fmr.hwpage_size; e_fmr->fmr_page_size = save_fmr.fmr_page_size; e_fmr->fmr_max_pages = save_fmr.fmr_max_pages; e_fmr->fmr_max_maps = save_fmr.fmr_max_maps; @@ -1363,8 +1447,6 @@ int ehca_unmap_one_fmr(struct ehca_shca *shca, memset(&pginfo, 0, sizeof(pginfo)); pginfo.type = EHCA_MR_PGI_FMR; - pginfo.num_kpages = 0; - pginfo.num_hwpages = 0; ret = ehca_reg_mr(shca, e_fmr, NULL, (e_fmr->fmr_max_pages * e_fmr->fmr_page_size), e_fmr->acl, e_pd, &pginfo, &tmp_lkey, @@ -1373,7 +1455,6 @@ int ehca_unmap_one_fmr(struct ehca_shca *shca, u32 offset = (u64)(&e_fmr->flags) - (u64)e_fmr; memcpy(&e_fmr->flags, &(save_mr.flags), sizeof(struct ehca_mr) - offset); - goto ehca_unmap_one_fmr_exit0; } ehca_unmap_one_fmr_exit0: @@ -1401,7 +1482,7 @@ int ehca_reg_smr(struct ehca_shca *shca, struct ehca_mr_hipzout_parms hipzout; ehca_mrmw_map_acl(acl, &hipz_acl); - ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl); + ehca_mrmw_set_pgsize_hipz_acl(e_origmr->hwpage_size, &hipz_acl); h_ret = hipz_h_register_smr(shca->ipz_hca_handle, e_newmr, e_origmr, (u64)iova_start, hipz_acl, e_pd->fw_pd, @@ -1420,6 +1501,7 @@ int ehca_reg_smr(struct ehca_shca *shca, /* successful registration */ e_newmr->num_kpages = e_origmr->num_kpages; e_newmr->num_hwpages = e_origmr->num_hwpages; + e_newmr->hwpage_size = e_origmr->hwpage_size; e_newmr->start = iova_start; e_newmr->size = e_origmr->size; e_newmr->acl = acl; @@ -1452,6 +1534,7 @@ int ehca_reg_internal_maxmr( struct ib_phys_buf ib_pbuf; u32 num_kpages; u32 num_hwpages; + u64 hw_pgsize; e_mr = ehca_mr_new(); if (!e_mr) { @@ -1468,13 +1551,15 @@ int ehca_reg_internal_maxmr( ib_pbuf.size = size_maxmr; num_kpages = NUM_CHUNKS(((u64)iova_start % PAGE_SIZE) + size_maxmr, PAGE_SIZE); - num_hwpages = NUM_CHUNKS(((u64)iova_start % EHCA_PAGESIZE) + size_maxmr, - EHCA_PAGESIZE); + hw_pgsize = ehca_get_max_hwpage_size(shca); + num_hwpages = NUM_CHUNKS(((u64)iova_start % hw_pgsize) + size_maxmr, + hw_pgsize); memset(&pginfo, 0, sizeof(pginfo)); pginfo.type = EHCA_MR_PGI_PHYS; pginfo.num_kpages = num_kpages; pginfo.num_hwpages = num_hwpages; + pginfo.hwpage_size = hw_pgsize; pginfo.u.phy.num_phys_buf = 1; pginfo.u.phy.phys_buf_array = &ib_pbuf; @@ -1523,7 +1608,7 @@ int ehca_reg_maxmr(struct ehca_shca *shca, struct ehca_mr_hipzout_parms hipzout; ehca_mrmw_map_acl(acl, &hipz_acl); - ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl); + ehca_mrmw_set_pgsize_hipz_acl(e_origmr->hwpage_size, &hipz_acl); h_ret = hipz_h_register_smr(shca->ipz_hca_handle, e_newmr, e_origmr, (u64)iova_start, hipz_acl, e_pd->fw_pd, @@ -1539,6 +1624,7 @@ int ehca_reg_maxmr(struct ehca_shca *shca, /* successful registration */ e_newmr->num_kpages = e_origmr->num_kpages; e_newmr->num_hwpages = e_origmr->num_hwpages; + e_newmr->hwpage_size = e_origmr->hwpage_size; e_newmr->start = iova_start; e_newmr->size = e_origmr->size; e_newmr->acl = acl; @@ -1684,6 +1770,7 @@ static int ehca_set_pagebuf_user1(struct ehca_mr_pginfo *pginfo, u64 pgaddr; u32 i = 0; u32 j = 0; + int hwpages_per_kpage = PAGE_SIZE / pginfo->hwpage_size; /* loop over desired chunk entries */ chunk = pginfo->u.usr.next_chunk; @@ -1695,7 +1782,7 @@ static int ehca_set_pagebuf_user1(struct ehca_mr_pginfo *pginfo, << PAGE_SHIFT ; *kpage = phys_to_abs(pgaddr + (pginfo->next_hwpage * - EHCA_PAGESIZE)); + pginfo->hwpage_size)); if ( !(*kpage) ) { ehca_gen_err("pgaddr=%lx " "chunk->page_list[i]=%lx " @@ -1708,8 +1795,7 @@ static int ehca_set_pagebuf_user1(struct ehca_mr_pginfo *pginfo, (pginfo->hwpage_cnt)++; (pginfo->next_hwpage)++; kpage++; - if (pginfo->next_hwpage % - (PAGE_SIZE / EHCA_PAGESIZE) == 0) { + if (pginfo->next_hwpage % hwpages_per_kpage == 0) { (pginfo->kpage_cnt)++; (pginfo->u.usr.next_nmap)++; pginfo->next_hwpage = 0; @@ -1738,6 +1824,143 @@ static int ehca_set_pagebuf_user1(struct ehca_mr_pginfo *pginfo, return ret; } +/* + * check given pages for contiguous layout + * last page addr is returned in prev_pgaddr for further check + */ +static int ehca_check_kpages_per_ate(struct scatterlist *page_list, + int start_idx, int end_idx, + u64 *prev_pgaddr) +{ + int t; + for (t = start_idx; t <= end_idx; t++) { + u64 pgaddr = page_to_pfn(page_list[t].page) << PAGE_SHIFT; + ehca_gen_dbg("chunk_page=%lx value=%016lx", pgaddr, + *(u64 *)abs_to_virt(phys_to_abs(pgaddr))); + if (pgaddr - PAGE_SIZE != *prev_pgaddr) { + ehca_gen_err("uncontiguous page found pgaddr=%lx " + "prev_pgaddr=%lx page_list_i=%x", + pgaddr, *prev_pgaddr, t); + return -EINVAL; + } + *prev_pgaddr = pgaddr; + } + return 0; +} + +/* PAGE_SIZE < pginfo->hwpage_size */ +static int ehca_set_pagebuf_user2(struct ehca_mr_pginfo *pginfo, + u32 number, + u64 *kpage) +{ + int ret = 0; + struct ib_umem_chunk *prev_chunk; + struct ib_umem_chunk *chunk; + u64 pgaddr, prev_pgaddr; + u32 i = 0; + u32 j = 0; + int kpages_per_hwpage = pginfo->hwpage_size / PAGE_SIZE; + int nr_kpages = kpages_per_hwpage; + + /* loop over desired chunk entries */ + chunk = pginfo->u.usr.next_chunk; + prev_chunk = pginfo->u.usr.next_chunk; + list_for_each_entry_continue( + chunk, (&(pginfo->u.usr.region->chunk_list)), list) { + for (i = pginfo->u.usr.next_nmap; i < chunk->nmap; ) { + if (nr_kpages == kpages_per_hwpage) { + pgaddr = ( page_to_pfn(chunk->page_list[i].page) + << PAGE_SHIFT ); + *kpage = phys_to_abs(pgaddr); + if ( !(*kpage) ) { + ehca_gen_err("pgaddr=%lx i=%x", + pgaddr, i); + ret = -EFAULT; + return ret; + } + /* + * The first page in a hwpage must be aligned; + * the first MR page is exempt from this rule. + */ + if (pgaddr & (pginfo->hwpage_size - 1)) { + if (pginfo->hwpage_cnt) { + ehca_gen_err( + "invalid alignment " + "pgaddr=%lx i=%x " + "mr_pgsize=%lx", + pgaddr, i, + pginfo->hwpage_size); + ret = -EFAULT; + return ret; + } + /* first MR page */ + pginfo->kpage_cnt = + (pgaddr & + (pginfo->hwpage_size - 1)) >> + PAGE_SHIFT; + nr_kpages -= pginfo->kpage_cnt; + *kpage = phys_to_abs( + pgaddr & + ~(pginfo->hwpage_size - 1)); + } + ehca_gen_dbg("kpage=%lx chunk_page=%lx " + "value=%016lx", *kpage, pgaddr, + *(u64 *)abs_to_virt( + phys_to_abs(pgaddr))); + prev_pgaddr = pgaddr; + i++; + pginfo->kpage_cnt++; + pginfo->u.usr.next_nmap++; + nr_kpages--; + if (!nr_kpages) + goto next_kpage; + continue; + } + if (i + nr_kpages > chunk->nmap) { + ret = ehca_check_kpages_per_ate( + chunk->page_list, i, + chunk->nmap - 1, &prev_pgaddr); + if (ret) return ret; + pginfo->kpage_cnt += chunk->nmap - i; + pginfo->u.usr.next_nmap += chunk->nmap - i; + nr_kpages -= chunk->nmap - i; + break; + } + + ret = ehca_check_kpages_per_ate(chunk->page_list, i, + i + nr_kpages - 1, + &prev_pgaddr); + if (ret) return ret; + i += nr_kpages; + pginfo->kpage_cnt += nr_kpages; + pginfo->u.usr.next_nmap += nr_kpages; +next_kpage: + nr_kpages = kpages_per_hwpage; + (pginfo->hwpage_cnt)++; + kpage++; + j++; + if (j >= number) break; + } + if ((pginfo->u.usr.next_nmap >= chunk->nmap) && + (j >= number)) { + pginfo->u.usr.next_nmap = 0; + prev_chunk = chunk; + break; + } else if (pginfo->u.usr.next_nmap >= chunk->nmap) { + pginfo->u.usr.next_nmap = 0; + prev_chunk = chunk; + } else if (j >= number) + break; + else + prev_chunk = chunk; + } + pginfo->u.usr.next_chunk = + list_prepare_entry(prev_chunk, + (&(pginfo->u.usr.region->chunk_list)), + list); + return ret; +} + int ehca_set_pagebuf_phys(struct ehca_mr_pginfo *pginfo, u32 number, u64 *kpage) @@ -1750,9 +1973,10 @@ int ehca_set_pagebuf_phys(struct ehca_mr_pginfo *pginfo, /* loop over desired phys_buf_array entries */ while (i < number) { pbuf = pginfo->u.phy.phys_buf_array + pginfo->u.phy.next_buf; - num_hw = NUM_CHUNKS((pbuf->addr % EHCA_PAGESIZE) + - pbuf->size, EHCA_PAGESIZE); - offs_hw = (pbuf->addr & ~PAGE_MASK) / EHCA_PAGESIZE; + num_hw = NUM_CHUNKS((pbuf->addr % pginfo->hwpage_size) + + pbuf->size, pginfo->hwpage_size); + offs_hw = (pbuf->addr & ~(pginfo->hwpage_size - 1)) / + pginfo->hwpage_size; while (pginfo->next_hwpage < offs_hw + num_hw) { /* sanity check */ if ((pginfo->kpage_cnt >= pginfo->num_kpages) || @@ -1768,21 +1992,23 @@ int ehca_set_pagebuf_phys(struct ehca_mr_pginfo *pginfo, return -EFAULT; } *kpage = phys_to_abs( - (pbuf->addr & EHCA_PAGEMASK) - + (pginfo->next_hwpage * EHCA_PAGESIZE)); + (pbuf->addr & ~(pginfo->hwpage_size - 1)) + + (pginfo->next_hwpage * pginfo->hwpage_size)); if ( !(*kpage) && pbuf->addr ) { - ehca_gen_err("pbuf->addr=%lx " - "pbuf->size=%lx " + ehca_gen_err("pbuf->addr=%lx pbuf->size=%lx " "next_hwpage=%lx", pbuf->addr, - pbuf->size, - pginfo->next_hwpage); + pbuf->size, pginfo->next_hwpage); return -EFAULT; } (pginfo->hwpage_cnt)++; (pginfo->next_hwpage)++; - if (pginfo->next_hwpage % - (PAGE_SIZE / EHCA_PAGESIZE) == 0) - (pginfo->kpage_cnt)++; + if (PAGE_SIZE >= pginfo->hwpage_size) { + if (pginfo->next_hwpage % + (PAGE_SIZE / pginfo->hwpage_size) == 0) + (pginfo->kpage_cnt)++; + } else + pginfo->kpage_cnt += pginfo->hwpage_size / + PAGE_SIZE; kpage++; i++; if (i >= number) break; @@ -1806,8 +2032,8 @@ int ehca_set_pagebuf_fmr(struct ehca_mr_pginfo *pginfo, /* loop over desired page_list entries */ fmrlist = pginfo->u.fmr.page_list + pginfo->u.fmr.next_listelem; for (i = 0; i < number; i++) { - *kpage = phys_to_abs((*fmrlist & EHCA_PAGEMASK) + - pginfo->next_hwpage * EHCA_PAGESIZE); + *kpage = phys_to_abs((*fmrlist & ~(pginfo->hwpage_size - 1)) + + pginfo->next_hwpage * pginfo->hwpage_size); if ( !(*kpage) ) { ehca_gen_err("*fmrlist=%lx fmrlist=%p " "next_listelem=%lx next_hwpage=%lx", @@ -1817,15 +2043,38 @@ int ehca_set_pagebuf_fmr(struct ehca_mr_pginfo *pginfo, return -EFAULT; } (pginfo->hwpage_cnt)++; - (pginfo->next_hwpage)++; - kpage++; - if (pginfo->next_hwpage % - (pginfo->u.fmr.fmr_pgsize / EHCA_PAGESIZE) == 0) { - (pginfo->kpage_cnt)++; - (pginfo->u.fmr.next_listelem)++; - fmrlist++; - pginfo->next_hwpage = 0; + if (pginfo->u.fmr.fmr_pgsize >= pginfo->hwpage_size) { + if (pginfo->next_hwpage % + (pginfo->u.fmr.fmr_pgsize / + pginfo->hwpage_size) == 0) { + (pginfo->kpage_cnt)++; + (pginfo->u.fmr.next_listelem)++; + fmrlist++; + pginfo->next_hwpage = 0; + } else + (pginfo->next_hwpage)++; + } else { + unsigned int cnt_per_hwpage = pginfo->hwpage_size / + pginfo->u.fmr.fmr_pgsize; + unsigned int j; + u64 prev = *kpage; + /* check if adrs are contiguous */ + for (j = 1; j < cnt_per_hwpage; j++) { + u64 p = phys_to_abs(fmrlist[j] & + ~(pginfo->hwpage_size - 1)); + if (prev + pginfo->u.fmr.fmr_pgsize != p) { + ehca_gen_err("uncontiguous fmr pages " + "found prev=%lx p=%lx " + "idx=%x", prev, p, i + j); + return -EINVAL; + } + prev = p; + } + pginfo->kpage_cnt += cnt_per_hwpage; + pginfo->u.fmr.next_listelem += cnt_per_hwpage; + fmrlist += cnt_per_hwpage; } + kpage++; } return ret; } @@ -1842,7 +2091,9 @@ int ehca_set_pagebuf(struct ehca_mr_pginfo *pginfo, ret = ehca_set_pagebuf_phys(pginfo, number, kpage); break; case EHCA_MR_PGI_USER: - ret = ehca_set_pagebuf_user1(pginfo, number, kpage); + ret = PAGE_SIZE >= pginfo->hwpage_size ? + ehca_set_pagebuf_user1(pginfo, number, kpage) : + ehca_set_pagebuf_user2(pginfo, number, kpage); break; case EHCA_MR_PGI_FMR: ret = ehca_set_pagebuf_fmr(pginfo, number, kpage); @@ -1895,9 +2146,9 @@ void ehca_mrmw_map_acl(int ib_acl, /*----------------------------------------------------------------------*/ /* sets page size in hipz access control for MR/MW. */ -void ehca_mrmw_set_pgsize_hipz_acl(u32 *hipz_acl) /*INOUT*/ +void ehca_mrmw_set_pgsize_hipz_acl(u32 pgsize, u32 *hipz_acl) /*INOUT*/ { - return; /* HCA supports only 4k */ + *hipz_acl |= (ehca_encode_hwpage_size(pgsize) << 24); } /* end ehca_mrmw_set_pgsize_hipz_acl() */ /*----------------------------------------------------------------------*/ diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.h b/drivers/infiniband/hw/ehca/ehca_mrmw.h index 24f13fe3708..bc8f4e31c12 100644 --- a/drivers/infiniband/hw/ehca/ehca_mrmw.h +++ b/drivers/infiniband/hw/ehca/ehca_mrmw.h @@ -111,7 +111,7 @@ int ehca_mr_is_maxmr(u64 size, void ehca_mrmw_map_acl(int ib_acl, u32 *hipz_acl); -void ehca_mrmw_set_pgsize_hipz_acl(u32 *hipz_acl); +void ehca_mrmw_set_pgsize_hipz_acl(u32 pgsize, u32 *hipz_acl); void ehca_mrmw_reverse_map_acl(const u32 *hipz_acl, int *ib_acl); diff --git a/drivers/infiniband/hw/ehca/ehca_pd.c b/drivers/infiniband/hw/ehca/ehca_pd.c index c85312ad292..3dafd7ff36c 100644 --- a/drivers/infiniband/hw/ehca/ehca_pd.c +++ b/drivers/infiniband/hw/ehca/ehca_pd.c @@ -49,6 +49,7 @@ struct ib_pd *ehca_alloc_pd(struct ib_device *device, struct ib_ucontext *context, struct ib_udata *udata) { struct ehca_pd *pd; + int i; pd = kmem_cache_zalloc(pd_cache, GFP_KERNEL); if (!pd) { @@ -58,6 +59,11 @@ struct ib_pd *ehca_alloc_pd(struct ib_device *device, } pd->ownpid = current->tgid; + for (i = 0; i < 2; i++) { + INIT_LIST_HEAD(&pd->free[i]); + INIT_LIST_HEAD(&pd->full[i]); + } + mutex_init(&pd->lock); /* * Kernel PD: when device = -1, 0 @@ -81,6 +87,9 @@ int ehca_dealloc_pd(struct ib_pd *pd) { u32 cur_pid = current->tgid; struct ehca_pd *my_pd = container_of(pd, struct ehca_pd, ib_pd); + int i, leftovers = 0; + extern struct kmem_cache *small_qp_cache; + struct ipz_small_queue_page *page, *tmp; if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context && my_pd->ownpid != cur_pid) { @@ -89,8 +98,20 @@ int ehca_dealloc_pd(struct ib_pd *pd) return -EINVAL; } - kmem_cache_free(pd_cache, - container_of(pd, struct ehca_pd, ib_pd)); + for (i = 0; i < 2; i++) { + list_splice(&my_pd->full[i], &my_pd->free[i]); + list_for_each_entry_safe(page, tmp, &my_pd->free[i], list) { + leftovers = 1; + free_page(page->page); + kmem_cache_free(small_qp_cache, page); + } + } + + if (leftovers) + ehca_warn(pd->device, + "Some small queue pages were not freed"); + + kmem_cache_free(pd_cache, my_pd); return 0; } diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c index a3146e696c5..b178cba9634 100644 --- a/drivers/infiniband/hw/ehca/ehca_qp.c +++ b/drivers/infiniband/hw/ehca/ehca_qp.c @@ -275,34 +275,39 @@ static inline void queue2resp(struct ipzu_queue_resp *resp, resp->toggle_state = queue->toggle_state; } -static inline int ll_qp_msg_size(int nr_sge) -{ - return 128 << nr_sge; -} - /* * init_qp_queue initializes/constructs r/squeue and registers queue pages. */ static inline int init_qp_queue(struct ehca_shca *shca, + struct ehca_pd *pd, struct ehca_qp *my_qp, struct ipz_queue *queue, int q_type, u64 expected_hret, - int nr_q_pages, - int wqe_size, - int nr_sges) + struct ehca_alloc_queue_parms *parms, + int wqe_size) { - int ret, cnt, ipz_rc; + int ret, cnt, ipz_rc, nr_q_pages; void *vpage; u64 rpage, h_ret; struct ib_device *ib_dev = &shca->ib_device; struct ipz_adapter_handle ipz_hca_handle = shca->ipz_hca_handle; - if (!nr_q_pages) + if (!parms->queue_size) return 0; - ipz_rc = ipz_queue_ctor(queue, nr_q_pages, EHCA_PAGESIZE, - wqe_size, nr_sges); + if (parms->is_small) { + nr_q_pages = 1; + ipz_rc = ipz_queue_ctor(pd, queue, nr_q_pages, + 128 << parms->page_size, + wqe_size, parms->act_nr_sges, 1); + } else { + nr_q_pages = parms->queue_size; + ipz_rc = ipz_queue_ctor(pd, queue, nr_q_pages, + EHCA_PAGESIZE, wqe_size, + parms->act_nr_sges, 0); + } + if (!ipz_rc) { ehca_err(ib_dev, "Cannot allocate page for queue. ipz_rc=%x", ipz_rc); @@ -323,7 +328,7 @@ static inline int init_qp_queue(struct ehca_shca *shca, h_ret = hipz_h_register_rpage_qp(ipz_hca_handle, my_qp->ipz_qp_handle, NULL, 0, q_type, - rpage, 1, + rpage, parms->is_small ? 0 : 1, my_qp->galpas.kernel); if (cnt == (nr_q_pages - 1)) { /* last page! */ if (h_ret != expected_hret) { @@ -354,19 +359,55 @@ static inline int init_qp_queue(struct ehca_shca *shca, return 0; init_qp_queue1: - ipz_queue_dtor(queue); + ipz_queue_dtor(pd, queue); return ret; } +static inline int ehca_calc_wqe_size(int act_nr_sge, int is_llqp) +{ + if (is_llqp) + return 128 << act_nr_sge; + else + return offsetof(struct ehca_wqe, + u.nud.sg_list[act_nr_sge]); +} + +static void ehca_determine_small_queue(struct ehca_alloc_queue_parms *queue, + int req_nr_sge, int is_llqp) +{ + u32 wqe_size, q_size; + int act_nr_sge = req_nr_sge; + + if (!is_llqp) + /* round up #SGEs so WQE size is a power of 2 */ + for (act_nr_sge = 4; act_nr_sge <= 252; + act_nr_sge = 4 + 2 * act_nr_sge) + if (act_nr_sge >= req_nr_sge) + break; + + wqe_size = ehca_calc_wqe_size(act_nr_sge, is_llqp); + q_size = wqe_size * (queue->max_wr + 1); + + if (q_size <= 512) + queue->page_size = 2; + else if (q_size <= 1024) + queue->page_size = 3; + else + queue->page_size = 0; + + queue->is_small = (queue->page_size != 0); +} + /* * Create an ib_qp struct that is either a QP or an SRQ, depending on * the value of the is_srq parameter. If init_attr and srq_init_attr share * fields, the field out of init_attr is used. */ -struct ehca_qp *internal_create_qp(struct ib_pd *pd, - struct ib_qp_init_attr *init_attr, - struct ib_srq_init_attr *srq_init_attr, - struct ib_udata *udata, int is_srq) +static struct ehca_qp *internal_create_qp( + struct ib_pd *pd, + struct ib_qp_init_attr *init_attr, + struct ib_srq_init_attr *srq_init_attr, + struct ib_udata *udata, int is_srq) { struct ehca_qp *my_qp; struct ehca_pd *my_pd = container_of(pd, struct ehca_pd, ib_pd); @@ -552,10 +593,20 @@ struct ehca_qp *internal_create_qp(struct ib_pd *pd, if (my_qp->recv_cq) parms.recv_cq_handle = my_qp->recv_cq->ipz_cq_handle; - parms.max_send_wr = init_attr->cap.max_send_wr; - parms.max_recv_wr = init_attr->cap.max_recv_wr; - parms.max_send_sge = max_send_sge; - parms.max_recv_sge = max_recv_sge; + parms.squeue.max_wr = init_attr->cap.max_send_wr; + parms.rqueue.max_wr = init_attr->cap.max_recv_wr; + parms.squeue.max_sge = max_send_sge; + parms.rqueue.max_sge = max_recv_sge; + + if (EHCA_BMASK_GET(HCA_CAP_MINI_QP, shca->hca_cap) + && !(context && udata)) { /* no small QP support in userspace ATM */ + ehca_determine_small_queue( + &parms.squeue, max_send_sge, is_llqp); + ehca_determine_small_queue( + &parms.rqueue, max_recv_sge, is_llqp); + parms.qp_storage = + (parms.squeue.is_small || parms.rqueue.is_small); + } h_ret = hipz_h_alloc_resource_qp(shca->ipz_hca_handle, &parms); if (h_ret != H_SUCCESS) { @@ -569,50 +620,33 @@ struct ehca_qp *internal_create_qp(struct ib_pd *pd, my_qp->ipz_qp_handle = parms.qp_handle; my_qp->galpas = parms.galpas; + swqe_size = ehca_calc_wqe_size(parms.squeue.act_nr_sges, is_llqp); + rwqe_size = ehca_calc_wqe_size(parms.rqueue.act_nr_sges, is_llqp); + switch (qp_type) { case IB_QPT_RC: - if (!is_llqp) { - swqe_size = offsetof(struct ehca_wqe, u.nud.sg_list[ - (parms.act_nr_send_sges)]); - rwqe_size = offsetof(struct ehca_wqe, u.nud.sg_list[ - (parms.act_nr_recv_sges)]); - } else { /* for LLQP we need to use msg size, not wqe size */ - swqe_size = ll_qp_msg_size(max_send_sge); - rwqe_size = ll_qp_msg_size(max_recv_sge); - parms.act_nr_send_sges = 1; - parms.act_nr_recv_sges = 1; + if (is_llqp) { + parms.squeue.act_nr_sges = 1; + parms.rqueue.act_nr_sges = 1; } break; - case IB_QPT_UC: - swqe_size = offsetof(struct ehca_wqe, - u.nud.sg_list[parms.act_nr_send_sges]); - rwqe_size = offsetof(struct ehca_wqe, - u.nud.sg_list[parms.act_nr_recv_sges]); - break; - case IB_QPT_UD: case IB_QPT_GSI: case IB_QPT_SMI: + /* UD circumvention */ if (is_llqp) { - swqe_size = ll_qp_msg_size(parms.act_nr_send_sges); - rwqe_size = ll_qp_msg_size(parms.act_nr_recv_sges); - parms.act_nr_send_sges = 1; - parms.act_nr_recv_sges = 1; + parms.squeue.act_nr_sges = 1; + parms.rqueue.act_nr_sges = 1; } else { - /* UD circumvention */ - parms.act_nr_send_sges -= 2; - parms.act_nr_recv_sges -= 2; - swqe_size = offsetof(struct ehca_wqe, u.ud_av.sg_list[ - parms.act_nr_send_sges]); - rwqe_size = offsetof(struct ehca_wqe, u.ud_av.sg_list[ - parms.act_nr_recv_sges]); + parms.squeue.act_nr_sges -= 2; + parms.rqueue.act_nr_sges -= 2; } if (IB_QPT_GSI == qp_type || IB_QPT_SMI == qp_type) { - parms.act_nr_send_wqes = init_attr->cap.max_send_wr; - parms.act_nr_recv_wqes = init_attr->cap.max_recv_wr; - parms.act_nr_send_sges = init_attr->cap.max_send_sge; - parms.act_nr_recv_sges = init_attr->cap.max_recv_sge; + parms.squeue.act_nr_wqes = init_attr->cap.max_send_wr; + parms.rqueue.act_nr_wqes = init_attr->cap.max_recv_wr; + parms.squeue.act_nr_sges = init_attr->cap.max_send_sge; + parms.rqueue.act_nr_sges = init_attr->cap.max_recv_sge; ib_qp_num = (qp_type == IB_QPT_SMI) ? 0 : 1; } @@ -625,10 +659,9 @@ struct ehca_qp *internal_create_qp(struct ib_pd *pd, /* initialize r/squeue and register queue pages */ if (HAS_SQ(my_qp)) { ret = init_qp_queue( - shca, my_qp, &my_qp->ipz_squeue, 0, + shca, my_pd, my_qp, &my_qp->ipz_squeue, 0, HAS_RQ(my_qp) ? H_PAGE_REGISTERED : H_SUCCESS, - parms.nr_sq_pages, swqe_size, - parms.act_nr_send_sges); + &parms.squeue, swqe_size); if (ret) { ehca_err(pd->device, "Couldn't initialize squeue " "and pages ret=%x", ret); @@ -638,9 +671,8 @@ struct ehca_qp *internal_create_qp(struct ib_pd *pd, if (HAS_RQ(my_qp)) { ret = init_qp_queue( - shca, my_qp, &my_qp->ipz_rqueue, 1, - H_SUCCESS, parms.nr_rq_pages, rwqe_size, - parms.act_nr_recv_sges); + shca, my_pd, my_qp, &my_qp->ipz_rqueue, 1, + H_SUCCESS, &parms.rqueue, rwqe_size); if (ret) { ehca_err(pd->device, "Couldn't initialize rqueue " "and pages ret=%x", ret); @@ -670,10 +702,10 @@ struct ehca_qp *internal_create_qp(struct ib_pd *pd, } init_attr->cap.max_inline_data = 0; /* not supported yet */ - init_attr->cap.max_recv_sge = parms.act_nr_recv_sges; - init_attr->cap.max_recv_wr = parms.act_nr_recv_wqes; - init_attr->cap.max_send_sge = parms.act_nr_send_sges; - init_attr->cap.max_send_wr = parms.act_nr_send_wqes; + init_attr->cap.max_recv_sge = parms.rqueue.act_nr_sges; + init_attr->cap.max_recv_wr = parms.rqueue.act_nr_wqes; + init_attr->cap.max_send_sge = parms.squeue.act_nr_sges; + init_attr->cap.max_send_wr = parms.squeue.act_nr_wqes; my_qp->init_attr = *init_attr; /* NOTE: define_apq0() not supported yet */ @@ -707,6 +739,8 @@ struct ehca_qp *internal_create_qp(struct ib_pd *pd, resp.ext_type = my_qp->ext_type; resp.qkey = my_qp->qkey; resp.real_qp_num = my_qp->real_qp_num; + resp.ipz_rqueue.offset = my_qp->ipz_rqueue.offset; + resp.ipz_squeue.offset = my_qp->ipz_squeue.offset; if (HAS_SQ(my_qp)) queue2resp(&resp.ipz_squeue, &my_qp->ipz_squeue); if (HAS_RQ(my_qp)) @@ -723,11 +757,11 @@ struct ehca_qp *internal_create_qp(struct ib_pd *pd, create_qp_exit4: if (HAS_RQ(my_qp)) - ipz_queue_dtor(&my_qp->ipz_rqueue); + ipz_queue_dtor(my_pd, &my_qp->ipz_rqueue); create_qp_exit3: if (HAS_SQ(my_qp)) - ipz_queue_dtor(&my_qp->ipz_squeue); + ipz_queue_dtor(my_pd, &my_qp->ipz_squeue); create_qp_exit2: hipz_h_destroy_qp(shca->ipz_hca_handle, my_qp); @@ -752,8 +786,8 @@ struct ib_qp *ehca_create_qp(struct ib_pd *pd, return IS_ERR(ret) ? (struct ib_qp *)ret : &ret->ib_qp; } -int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp, - struct ib_uobject *uobject); +static int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp, + struct ib_uobject *uobject); struct ib_srq *ehca_create_srq(struct ib_pd *pd, struct ib_srq_init_attr *srq_init_attr, @@ -1669,8 +1703,8 @@ query_srq_exit1: return ret; } -int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp, - struct ib_uobject *uobject) +static int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp, + struct ib_uobject *uobject) { struct ehca_shca *shca = container_of(dev, struct ehca_shca, ib_device); struct ehca_pd *my_pd = container_of(my_qp->ib_qp.pd, struct ehca_pd, @@ -1734,9 +1768,9 @@ int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp, } if (HAS_RQ(my_qp)) - ipz_queue_dtor(&my_qp->ipz_rqueue); + ipz_queue_dtor(my_pd, &my_qp->ipz_rqueue); if (HAS_SQ(my_qp)) - ipz_queue_dtor(&my_qp->ipz_squeue); + ipz_queue_dtor(my_pd, &my_qp->ipz_squeue); kmem_cache_free(qp_cache, my_qp); return 0; } diff --git a/drivers/infiniband/hw/ehca/ehca_tools.h b/drivers/infiniband/hw/ehca/ehca_tools.h index 678b8139186..57c77a715f4 100644 --- a/drivers/infiniband/hw/ehca/ehca_tools.h +++ b/drivers/infiniband/hw/ehca/ehca_tools.h @@ -154,24 +154,7 @@ extern int ehca_debug_level; #define EHCA_BMASK_GET(mask, value) \ (EHCA_BMASK_MASK(mask) & (((u64)(value)) >> EHCA_BMASK_SHIFTPOS(mask))) - /* Converts ehca to ib return code */ -static inline int ehca2ib_return_code(u64 ehca_rc) -{ - switch (ehca_rc) { - case H_SUCCESS: - return 0; - case H_RESOURCE: /* Resource in use */ - case H_BUSY: - return -EBUSY; - case H_NOT_ENOUGH_RESOURCES: /* insufficient resources */ - case H_CONSTRAINED: /* resource constraint */ - case H_NO_MEM: - return -ENOMEM; - default: - return -EINVAL; - } -} - +int ehca2ib_return_code(u64 ehca_rc); #endif /* EHCA_TOOLS_H */ diff --git a/drivers/infiniband/hw/ehca/ehca_uverbs.c b/drivers/infiniband/hw/ehca/ehca_uverbs.c index 05c415744e3..4bc687fdf53 100644 --- a/drivers/infiniband/hw/ehca/ehca_uverbs.c +++ b/drivers/infiniband/hw/ehca/ehca_uverbs.c @@ -149,7 +149,7 @@ static int ehca_mmap_queue(struct vm_area_struct *vma, struct ipz_queue *queue, ehca_gen_err("vm_insert_page() failed rc=%x", ret); return ret; } - start += PAGE_SIZE; + start += PAGE_SIZE; } vma->vm_private_data = mm_count; (*mm_count)++; diff --git a/drivers/infiniband/hw/ehca/hcp_if.c b/drivers/infiniband/hw/ehca/hcp_if.c index 3394e05f4b4..fdbfebea7d1 100644 --- a/drivers/infiniband/hw/ehca/hcp_if.c +++ b/drivers/infiniband/hw/ehca/hcp_if.c @@ -52,10 +52,13 @@ #define H_ALL_RES_QP_ENHANCED_OPS EHCA_BMASK_IBM(9, 11) #define H_ALL_RES_QP_PTE_PIN EHCA_BMASK_IBM(12, 12) #define H_ALL_RES_QP_SERVICE_TYPE EHCA_BMASK_IBM(13, 15) +#define H_ALL_RES_QP_STORAGE EHCA_BMASK_IBM(16, 17) #define H_ALL_RES_QP_LL_RQ_CQE_POSTING EHCA_BMASK_IBM(18, 18) #define H_ALL_RES_QP_LL_SQ_CQE_POSTING EHCA_BMASK_IBM(19, 21) #define H_ALL_RES_QP_SIGNALING_TYPE EHCA_BMASK_IBM(22, 23) #define H_ALL_RES_QP_UD_AV_LKEY_CTRL EHCA_BMASK_IBM(31, 31) +#define H_ALL_RES_QP_SMALL_SQ_PAGE_SIZE EHCA_BMASK_IBM(32, 35) +#define H_ALL_RES_QP_SMALL_RQ_PAGE_SIZE EHCA_BMASK_IBM(36, 39) #define H_ALL_RES_QP_RESOURCE_TYPE EHCA_BMASK_IBM(56, 63) #define H_ALL_RES_QP_MAX_OUTST_SEND_WR EHCA_BMASK_IBM(0, 15) @@ -299,6 +302,11 @@ u64 hipz_h_alloc_resource_qp(const struct ipz_adapter_handle adapter_handle, | EHCA_BMASK_SET(H_ALL_RES_QP_PTE_PIN, 0) | EHCA_BMASK_SET(H_ALL_RES_QP_SERVICE_TYPE, parms->servicetype) | EHCA_BMASK_SET(H_ALL_RES_QP_SIGNALING_TYPE, parms->sigtype) + | EHCA_BMASK_SET(H_ALL_RES_QP_STORAGE, parms->qp_storage) + | EHCA_BMASK_SET(H_ALL_RES_QP_SMALL_SQ_PAGE_SIZE, + parms->squeue.page_size) + | EHCA_BMASK_SET(H_ALL_RES_QP_SMALL_RQ_PAGE_SIZE, + parms->rqueue.page_size) | EHCA_BMASK_SET(H_ALL_RES_QP_LL_RQ_CQE_POSTING, !!(parms->ll_comp_flags & LLQP_RECV_COMP)) | EHCA_BMASK_SET(H_ALL_RES_QP_LL_SQ_CQE_POSTING, @@ -309,13 +317,13 @@ u64 hipz_h_alloc_resource_qp(const struct ipz_adapter_handle adapter_handle, max_r10_reg = EHCA_BMASK_SET(H_ALL_RES_QP_MAX_OUTST_SEND_WR, - parms->max_send_wr + 1) + parms->squeue.max_wr + 1) | EHCA_BMASK_SET(H_ALL_RES_QP_MAX_OUTST_RECV_WR, - parms->max_recv_wr + 1) + parms->rqueue.max_wr + 1) | EHCA_BMASK_SET(H_ALL_RES_QP_MAX_SEND_SGE, - parms->max_send_sge) + parms->squeue.max_sge) | EHCA_BMASK_SET(H_ALL_RES_QP_MAX_RECV_SGE, - parms->max_recv_sge); + parms->rqueue.max_sge); r11 = EHCA_BMASK_SET(H_ALL_RES_QP_SRQ_QP_TOKEN, parms->srq_token); @@ -335,17 +343,17 @@ u64 hipz_h_alloc_resource_qp(const struct ipz_adapter_handle adapter_handle, parms->qp_handle.handle = outs[0]; parms->real_qp_num = (u32)outs[1]; - parms->act_nr_send_wqes = + parms->squeue.act_nr_wqes = (u16)EHCA_BMASK_GET(H_ALL_RES_QP_ACT_OUTST_SEND_WR, outs[2]); - parms->act_nr_recv_wqes = + parms->rqueue.act_nr_wqes = (u16)EHCA_BMASK_GET(H_ALL_RES_QP_ACT_OUTST_RECV_WR, outs[2]); - parms->act_nr_send_sges = + parms->squeue.act_nr_sges = (u8)EHCA_BMASK_GET(H_ALL_RES_QP_ACT_SEND_SGE, outs[3]); - parms->act_nr_recv_sges = + parms->rqueue.act_nr_sges = (u8)EHCA_BMASK_GET(H_ALL_RES_QP_ACT_RECV_SGE, outs[3]); - parms->nr_sq_pages = + parms->squeue.queue_size = (u32)EHCA_BMASK_GET(H_ALL_RES_QP_SQUEUE_SIZE_PAGES, outs[4]); - parms->nr_rq_pages = + parms->rqueue.queue_size = (u32)EHCA_BMASK_GET(H_ALL_RES_QP_RQUEUE_SIZE_PAGES, outs[4]); if (ret == H_SUCCESS) @@ -427,7 +435,8 @@ u64 hipz_h_register_rpage(const struct ipz_adapter_handle adapter_handle, { return ehca_plpar_hcall_norets(H_REGISTER_RPAGES, adapter_handle.handle, /* r4 */ - queue_type | pagesize << 8, /* r5 */ + (u64)queue_type | ((u64)pagesize) << 8, + /* r5 */ resource_handle, /* r6 */ logical_address_of_page, /* r7 */ count, /* r8 */ @@ -496,7 +505,7 @@ u64 hipz_h_register_rpage_qp(const struct ipz_adapter_handle adapter_handle, const u64 count, const struct h_galpa galpa) { - if (count != 1) { + if (count > 1) { ehca_gen_err("Page counter=%lx", count); return H_PARAMETER; } @@ -724,6 +733,9 @@ u64 hipz_h_alloc_resource_mr(const struct ipz_adapter_handle adapter_handle, u64 ret; u64 outs[PLPAR_HCALL9_BUFSIZE]; + ehca_gen_dbg("kernel PAGE_SIZE=%x access_ctrl=%016x " + "vaddr=%lx length=%lx", + (u32)PAGE_SIZE, access_ctrl, vaddr, length); ret = ehca_plpar_hcall9(H_ALLOC_RESOURCE, outs, adapter_handle.handle, /* r4 */ 5, /* r5 */ @@ -746,8 +758,22 @@ u64 hipz_h_register_rpage_mr(const struct ipz_adapter_handle adapter_handle, const u64 logical_address_of_page, const u64 count) { + extern int ehca_debug_level; u64 ret; + if (unlikely(ehca_debug_level >= 2)) { + if (count > 1) { + u64 *kpage; + int i; + kpage = (u64 *)abs_to_virt(logical_address_of_page); + for (i = 0; i < count; i++) + ehca_gen_dbg("kpage[%d]=%p", + i, (void *)kpage[i]); + } else + ehca_gen_dbg("kpage=%p", + (void *)logical_address_of_page); + } + if ((count > 1) && (logical_address_of_page & (EHCA_PAGESIZE-1))) { ehca_gen_err("logical_address_of_page not on a 4k boundary " "adapter_handle=%lx mr=%p mr_handle=%lx " diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.c b/drivers/infiniband/hw/ehca/ipz_pt_fn.c index 9606f13ed09..a090c679c39 100644 --- a/drivers/infiniband/hw/ehca/ipz_pt_fn.c +++ b/drivers/infiniband/hw/ehca/ipz_pt_fn.c @@ -40,6 +40,11 @@ #include "ehca_tools.h" #include "ipz_pt_fn.h" +#include "ehca_classes.h" + +#define PAGES_PER_KPAGE (PAGE_SIZE >> EHCA_PAGESHIFT) + +struct kmem_cache *small_qp_cache; void *ipz_qpageit_get_inc(struct ipz_queue *queue) { @@ -49,7 +54,7 @@ void *ipz_qpageit_get_inc(struct ipz_queue *queue) queue->current_q_offset -= queue->pagesize; ret = NULL; } - if (((u64)ret) % EHCA_PAGESIZE) { + if (((u64)ret) % queue->pagesize) { ehca_gen_err("ERROR!! not at PAGE-Boundary"); return NULL; } @@ -83,80 +88,195 @@ int ipz_queue_abs_to_offset(struct ipz_queue *queue, u64 addr, u64 *q_offset) return -EINVAL; } -int ipz_queue_ctor(struct ipz_queue *queue, - const u32 nr_of_pages, - const u32 pagesize, const u32 qe_size, const u32 nr_of_sg) +#if PAGE_SHIFT < EHCA_PAGESHIFT +#error Kernel pages must be at least as large than eHCA pages (4K) ! +#endif + +/* + * allocate pages for queue: + * outer loop allocates whole kernel pages (page aligned) and + * inner loop divides a kernel page into smaller hca queue pages + */ +static int alloc_queue_pages(struct ipz_queue *queue, const u32 nr_of_pages) { - int pages_per_kpage = PAGE_SIZE >> EHCA_PAGESHIFT; - int f; + int k, f = 0; + u8 *kpage; - if (pagesize > PAGE_SIZE) { - ehca_gen_err("FATAL ERROR: pagesize=%x is greater " - "than kernel page size", pagesize); - return 0; - } - if (!pages_per_kpage) { - ehca_gen_err("FATAL ERROR: invalid kernel page size. " - "pages_per_kpage=%x", pages_per_kpage); - return 0; - } - queue->queue_length = nr_of_pages * pagesize; - queue->queue_pages = vmalloc(nr_of_pages * sizeof(void *)); - if (!queue->queue_pages) { - ehca_gen_err("ERROR!! didn't get the memory"); - return 0; - } - memset(queue->queue_pages, 0, nr_of_pages * sizeof(void *)); - /* - * allocate pages for queue: - * outer loop allocates whole kernel pages (page aligned) and - * inner loop divides a kernel page into smaller hca queue pages - */ - f = 0; while (f < nr_of_pages) { - u8 *kpage = (u8 *)get_zeroed_page(GFP_KERNEL); - int k; + kpage = (u8 *)get_zeroed_page(GFP_KERNEL); if (!kpage) - goto ipz_queue_ctor_exit0; /*NOMEM*/ - for (k = 0; k < pages_per_kpage && f < nr_of_pages; k++) { - (queue->queue_pages)[f] = (struct ipz_page *)kpage; + goto out; + + for (k = 0; k < PAGES_PER_KPAGE && f < nr_of_pages; k++) { + queue->queue_pages[f] = (struct ipz_page *)kpage; kpage += EHCA_PAGESIZE; f++; } } + return 1; - queue->current_q_offset = 0; +out: + for (f = 0; f < nr_of_pages && queue->queue_pages[f]; + f += PAGES_PER_KPAGE) + free_page((unsigned long)(queue->queue_pages)[f]); + return 0; +} + +static int alloc_small_queue_page(struct ipz_queue *queue, struct ehca_pd *pd) +{ + int order = ilog2(queue->pagesize) - 9; + struct ipz_small_queue_page *page; + unsigned long bit; + + mutex_lock(&pd->lock); + + if (!list_empty(&pd->free[order])) + page = list_entry(pd->free[order].next, + struct ipz_small_queue_page, list); + else { + page = kmem_cache_zalloc(small_qp_cache, GFP_KERNEL); + if (!page) + goto out; + + page->page = get_zeroed_page(GFP_KERNEL); + if (!page->page) { + kmem_cache_free(small_qp_cache, page); + goto out; + } + + list_add(&page->list, &pd->free[order]); + } + + bit = find_first_zero_bit(page->bitmap, IPZ_SPAGE_PER_KPAGE >> order); + __set_bit(bit, page->bitmap); + page->fill++; + + if (page->fill == IPZ_SPAGE_PER_KPAGE >> order) + list_move(&page->list, &pd->full[order]); + + mutex_unlock(&pd->lock); + + queue->queue_pages[0] = (void *)(page->page | (bit << (order + 9))); + queue->small_page = page; + return 1; + +out: + ehca_err(pd->ib_pd.device, "failed to allocate small queue page"); + return 0; +} + +static void free_small_queue_page(struct ipz_queue *queue, struct ehca_pd *pd) +{ + int order = ilog2(queue->pagesize) - 9; + struct ipz_small_queue_page *page = queue->small_page; + unsigned long bit; + int free_page = 0; + + bit = ((unsigned long)queue->queue_pages[0] & PAGE_MASK) + >> (order + 9); + + mutex_lock(&pd->lock); + + __clear_bit(bit, page->bitmap); + page->fill--; + + if (page->fill == 0) { + list_del(&page->list); + free_page = 1; + } + + if (page->fill == (IPZ_SPAGE_PER_KPAGE >> order) - 1) + /* the page was full until we freed the chunk */ + list_move_tail(&page->list, &pd->free[order]); + + mutex_unlock(&pd->lock); + + if (free_page) { + free_page(page->page); + kmem_cache_free(small_qp_cache, page); + } +} + +int ipz_queue_ctor(struct ehca_pd *pd, struct ipz_queue *queue, + const u32 nr_of_pages, const u32 pagesize, + const u32 qe_size, const u32 nr_of_sg, + int is_small) +{ + if (pagesize > PAGE_SIZE) { + ehca_gen_err("FATAL ERROR: pagesize=%x " + "is greater than kernel page size", pagesize); + return 0; + } + + /* init queue fields */ + queue->queue_length = nr_of_pages * pagesize; + queue->pagesize = pagesize; queue->qe_size = qe_size; queue->act_nr_of_sg = nr_of_sg; - queue->pagesize = pagesize; + queue->current_q_offset = 0; queue->toggle_state = 1; - return 1; + queue->small_page = NULL; - ipz_queue_ctor_exit0: - ehca_gen_err("Couldn't get alloc pages queue=%p f=%x nr_of_pages=%x", - queue, f, nr_of_pages); - for (f = 0; f < nr_of_pages; f += pages_per_kpage) { - if (!(queue->queue_pages)[f]) - break; - free_page((unsigned long)(queue->queue_pages)[f]); + /* allocate queue page pointers */ + queue->queue_pages = vmalloc(nr_of_pages * sizeof(void *)); + if (!queue->queue_pages) { + ehca_gen_err("Couldn't allocate queue page list"); + return 0; } + memset(queue->queue_pages, 0, nr_of_pages * sizeof(void *)); + + /* allocate actual queue pages */ + if (is_small) { + if (!alloc_small_queue_page(queue, pd)) + goto ipz_queue_ctor_exit0; + } else + if (!alloc_queue_pages(queue, nr_of_pages)) + goto ipz_queue_ctor_exit0; + + return 1; + +ipz_queue_ctor_exit0: + ehca_gen_err("Couldn't alloc pages queue=%p " + "nr_of_pages=%x", queue, nr_of_pages); + vfree(queue->queue_pages); + return 0; } -int ipz_queue_dtor(struct ipz_queue *queue) +int ipz_queue_dtor(struct ehca_pd *pd, struct ipz_queue *queue) { - int pages_per_kpage = PAGE_SIZE >> EHCA_PAGESHIFT; - int g; - int nr_pages; + int i, nr_pages; if (!queue || !queue->queue_pages) { ehca_gen_dbg("queue or queue_pages is NULL"); return 0; } - nr_pages = queue->queue_length / queue->pagesize; - for (g = 0; g < nr_pages; g += pages_per_kpage) - free_page((unsigned long)(queue->queue_pages)[g]); + + if (queue->small_page) + free_small_queue_page(queue, pd); + else { + nr_pages = queue->queue_length / queue->pagesize; + for (i = 0; i < nr_pages; i += PAGES_PER_KPAGE) + free_page((unsigned long)queue->queue_pages[i]); + } + vfree(queue->queue_pages); return 1; } + +int ehca_init_small_qp_cache(void) +{ + small_qp_cache = kmem_cache_create("ehca_cache_small_qp", + sizeof(struct ipz_small_queue_page), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!small_qp_cache) + return -ENOMEM; + + return 0; +} + +void ehca_cleanup_small_qp_cache(void) +{ + kmem_cache_destroy(small_qp_cache); +} diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.h b/drivers/infiniband/hw/ehca/ipz_pt_fn.h index 39a4f64aff4..c6937a044e8 100644 --- a/drivers/infiniband/hw/ehca/ipz_pt_fn.h +++ b/drivers/infiniband/hw/ehca/ipz_pt_fn.h @@ -51,11 +51,25 @@ #include "ehca_tools.h" #include "ehca_qes.h" +struct ehca_pd; +struct ipz_small_queue_page; + /* struct generic ehca page */ struct ipz_page { u8 entries[EHCA_PAGESIZE]; }; +#define IPZ_SPAGE_PER_KPAGE (PAGE_SIZE / 512) + +struct ipz_small_queue_page { + unsigned long page; + unsigned long bitmap[IPZ_SPAGE_PER_KPAGE / BITS_PER_LONG]; + int fill; + void *mapped_addr; + u32 mmap_count; + struct list_head list; +}; + /* struct generic queue in linux kernel virtual memory (kv) */ struct ipz_queue { u64 current_q_offset; /* current queue entry */ @@ -66,7 +80,8 @@ struct ipz_queue { u32 queue_length; /* queue length allocated in bytes */ u32 pagesize; u32 toggle_state; /* toggle flag - per page */ - u32 dummy3; /* 64 bit alignment */ + u32 offset; /* save offset within page for small_qp */ + struct ipz_small_queue_page *small_page; }; /* @@ -188,9 +203,10 @@ struct ipz_qpt { * see ipz_qpt_ctor() * returns true if ok, false if out of memory */ -int ipz_queue_ctor(struct ipz_queue *queue, const u32 nr_of_pages, - const u32 pagesize, const u32 qe_size, - const u32 nr_of_sg); +int ipz_queue_ctor(struct ehca_pd *pd, struct ipz_queue *queue, + const u32 nr_of_pages, const u32 pagesize, + const u32 qe_size, const u32 nr_of_sg, + int is_small); /* * destructor for a ipz_queue_t @@ -198,7 +214,7 @@ int ipz_queue_ctor(struct ipz_queue *queue, const u32 nr_of_pages, * see ipz_queue_ctor() * returns true if ok, false if queue was NULL-ptr of free failed */ -int ipz_queue_dtor(struct ipz_queue *queue); +int ipz_queue_dtor(struct ehca_pd *pd, struct ipz_queue *queue); /* * constructor for a ipz_qpt_t, diff --git a/drivers/infiniband/hw/ipath/Makefile b/drivers/infiniband/hw/ipath/Makefile index ec2e603ea24..fe673882686 100644 --- a/drivers/infiniband/hw/ipath/Makefile +++ b/drivers/infiniband/hw/ipath/Makefile @@ -14,7 +14,6 @@ ib_ipath-y := \ ipath_init_chip.o \ ipath_intr.o \ ipath_keys.o \ - ipath_layer.o \ ipath_mad.o \ ipath_mmap.o \ ipath_mr.o \ diff --git a/drivers/infiniband/hw/ipath/ipath_layer.c b/drivers/infiniband/hw/ipath/ipath_layer.c deleted file mode 100644 index 82616b779e2..00000000000 --- a/drivers/infiniband/hw/ipath/ipath_layer.c +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved. - * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * These are the routines used by layered drivers, currently just the - * layered ethernet driver and verbs layer. - */ - -#include <linux/io.h> -#include <asm/byteorder.h> - -#include "ipath_kernel.h" -#include "ipath_layer.h" -#include "ipath_verbs.h" -#include "ipath_common.h" - -/* Acquire before ipath_devs_lock. */ -static DEFINE_MUTEX(ipath_layer_mutex); - -u16 ipath_layer_rcv_opcode; - -static int (*layer_intr)(void *, u32); -static int (*layer_rcv)(void *, void *, struct sk_buff *); -static int (*layer_rcv_lid)(void *, void *); - -static void *(*layer_add_one)(int, struct ipath_devdata *); -static void (*layer_remove_one)(void *); - -int __ipath_layer_intr(struct ipath_devdata *dd, u32 arg) -{ - int ret = -ENODEV; - - if (dd->ipath_layer.l_arg && layer_intr) - ret = layer_intr(dd->ipath_layer.l_arg, arg); - - return ret; -} - -int ipath_layer_intr(struct ipath_devdata *dd, u32 arg) -{ - int ret; - - mutex_lock(&ipath_layer_mutex); - - ret = __ipath_layer_intr(dd, arg); - - mutex_unlock(&ipath_layer_mutex); - - return ret; -} - -int __ipath_layer_rcv(struct ipath_devdata *dd, void *hdr, - struct sk_buff *skb) -{ - int ret = -ENODEV; - - if (dd->ipath_layer.l_arg && layer_rcv) - ret = layer_rcv(dd->ipath_layer.l_arg, hdr, skb); - - return ret; -} - -int __ipath_layer_rcv_lid(struct ipath_devdata *dd, void *hdr) -{ - int ret = -ENODEV; - - if (dd->ipath_layer.l_arg && layer_rcv_lid) - ret = layer_rcv_lid(dd->ipath_layer.l_arg, hdr); - - return ret; -} - -void ipath_layer_lid_changed(struct ipath_devdata *dd) -{ - mutex_lock(&ipath_layer_mutex); - - if (dd->ipath_layer.l_arg && layer_intr) - layer_intr(dd->ipath_layer.l_arg, IPATH_LAYER_INT_LID); - - mutex_unlock(&ipath_layer_mutex); -} - -void ipath_layer_add(struct ipath_devdata *dd) -{ - mutex_lock(&ipath_layer_mutex); - - if (layer_add_one) - dd->ipath_layer.l_arg = - layer_add_one(dd->ipath_unit, dd); - - mutex_unlock(&ipath_layer_mutex); -} - -void ipath_layer_remove(struct ipath_devdata *dd) -{ - mutex_lock(&ipath_layer_mutex); - - if (dd->ipath_layer.l_arg && layer_remove_one) { - layer_remove_one(dd->ipath_layer.l_arg); - dd->ipath_layer.l_arg = NULL; - } - - mutex_unlock(&ipath_layer_mutex); -} - -int ipath_layer_register(void *(*l_add)(int, struct ipath_devdata *), - void (*l_remove)(void *), - int (*l_intr)(void *, u32), - int (*l_rcv)(void *, void *, struct sk_buff *), - u16 l_rcv_opcode, - int (*l_rcv_lid)(void *, void *)) -{ - struct ipath_devdata *dd, *tmp; - unsigned long flags; - - mutex_lock(&ipath_layer_mutex); - - layer_add_one = l_add; - layer_remove_one = l_remove; - layer_intr = l_intr; - layer_rcv = l_rcv; - layer_rcv_lid = l_rcv_lid; - ipath_layer_rcv_opcode = l_rcv_opcode; - - spin_lock_irqsave(&ipath_devs_lock, flags); - - list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { - if (!(dd->ipath_flags & IPATH_INITTED)) - continue; - - if (dd->ipath_layer.l_arg) - continue; - - spin_unlock_irqrestore(&ipath_devs_lock, flags); - dd->ipath_layer.l_arg = l_add(dd->ipath_unit, dd); - spin_lock_irqsave(&ipath_devs_lock, flags); - } - - spin_unlock_irqrestore(&ipath_devs_lock, flags); - mutex_unlock(&ipath_layer_mutex); - - return 0; -} - -EXPORT_SYMBOL_GPL(ipath_layer_register); - -void ipath_layer_unregister(void) -{ - struct ipath_devdata *dd, *tmp; - unsigned long flags; - - mutex_lock(&ipath_layer_mutex); - spin_lock_irqsave(&ipath_devs_lock, flags); - - list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { - if (dd->ipath_layer.l_arg && layer_remove_one) { - spin_unlock_irqrestore(&ipath_devs_lock, flags); - layer_remove_one(dd->ipath_layer.l_arg); - spin_lock_irqsave(&ipath_devs_lock, flags); - dd->ipath_layer.l_arg = NULL; - } - } - - spin_unlock_irqrestore(&ipath_devs_lock, flags); - - layer_add_one = NULL; - layer_remove_one = NULL; - layer_intr = NULL; - layer_rcv = NULL; - layer_rcv_lid = NULL; - - mutex_unlock(&ipath_layer_mutex); -} - -EXPORT_SYMBOL_GPL(ipath_layer_unregister); - -int ipath_layer_open(struct ipath_devdata *dd, u32 * pktmax) -{ - int ret; - u32 intval = 0; - - mutex_lock(&ipath_layer_mutex); - - if (!dd->ipath_layer.l_arg) { - ret = -EINVAL; - goto bail; - } - - ret = ipath_setrcvhdrsize(dd, IPATH_HEADER_QUEUE_WORDS); - - if (ret < 0) - goto bail; - - *pktmax = dd->ipath_ibmaxlen; - - if (*dd->ipath_statusp & IPATH_STATUS_IB_READY) - intval |= IPATH_LAYER_INT_IF_UP; - if (dd->ipath_lid) - intval |= IPATH_LAYER_INT_LID; - if (dd->ipath_mlid) - intval |= IPATH_LAYER_INT_BCAST; - /* - * do this on open, in case low level is already up and - * just layered driver was reloaded, etc. - */ - if (intval) - layer_intr(dd->ipath_layer.l_arg, intval); - - ret = 0; -bail: - mutex_unlock(&ipath_layer_mutex); - - return ret; -} - -EXPORT_SYMBOL_GPL(ipath_layer_open); - -u16 ipath_layer_get_lid(struct ipath_devdata *dd) -{ - return dd->ipath_lid; -} - -EXPORT_SYMBOL_GPL(ipath_layer_get_lid); - -/** - * ipath_layer_get_mac - get the MAC address - * @dd: the infinipath device - * @mac: the MAC is put here - * - * This is the EUID-64 OUI octets (top 3), then - * skip the next 2 (which should both be zero or 0xff). - * The returned MAC is in network order - * mac points to at least 6 bytes of buffer - * We assume that by the time the LID is set, that the GUID is as valid - * as it's ever going to be, rather than adding yet another status bit. - */ - -int ipath_layer_get_mac(struct ipath_devdata *dd, u8 * mac) -{ - u8 *guid; - - guid = (u8 *) &dd->ipath_guid; - - mac[0] = guid[0]; - mac[1] = guid[1]; - mac[2] = guid[2]; - mac[3] = guid[5]; - mac[4] = guid[6]; - mac[5] = guid[7]; - if ((guid[3] || guid[4]) && !(guid[3] == 0xff && guid[4] == 0xff)) - ipath_dbg("Warning, guid bytes 3 and 4 not 0 or 0xffff: " - "%x %x\n", guid[3], guid[4]); - return 0; -} - -EXPORT_SYMBOL_GPL(ipath_layer_get_mac); - -u16 ipath_layer_get_bcast(struct ipath_devdata *dd) -{ - return dd->ipath_mlid; -} - -EXPORT_SYMBOL_GPL(ipath_layer_get_bcast); - -int ipath_layer_send_hdr(struct ipath_devdata *dd, struct ether_header *hdr) -{ - int ret = 0; - u32 __iomem *piobuf; - u32 plen, *uhdr; - size_t count; - __be16 vlsllnh; - - if (!(dd->ipath_flags & IPATH_RCVHDRSZ_SET)) { - ipath_dbg("send while not open\n"); - ret = -EINVAL; - } else - if ((dd->ipath_flags & (IPATH_LINKUNK | IPATH_LINKDOWN)) || - dd->ipath_lid == 0) { - /* - * lid check is for when sma hasn't yet configured - */ - ret = -ENETDOWN; - ipath_cdbg(VERBOSE, "send while not ready, " - "mylid=%u, flags=0x%x\n", - dd->ipath_lid, dd->ipath_flags); - } - - vlsllnh = *((__be16 *) hdr); - if (vlsllnh != htons(IPATH_LRH_BTH)) { - ipath_dbg("Warning: lrh[0] wrong (%x, not %x); " - "not sending\n", be16_to_cpu(vlsllnh), - IPATH_LRH_BTH); - ret = -EINVAL; - } - if (ret) - goto done; - - /* Get a PIO buffer to use. */ - piobuf = ipath_getpiobuf(dd, NULL); - if (piobuf == NULL) { - ret = -EBUSY; - goto done; - } - - plen = (sizeof(*hdr) >> 2); /* actual length */ - ipath_cdbg(EPKT, "0x%x+1w pio %p\n", plen, piobuf); - - writeq(plen+1, piobuf); /* len (+1 for pad) to pbc, no flags */ - ipath_flush_wc(); - piobuf += 2; - uhdr = (u32 *)hdr; - count = plen-1; /* amount we can copy before trigger word */ - __iowrite32_copy(piobuf, uhdr, count); - ipath_flush_wc(); - __raw_writel(uhdr[count], piobuf + count); - ipath_flush_wc(); /* ensure it's sent, now */ - - ipath_stats.sps_ether_spkts++; /* ether packet sent */ - -done: - return ret; -} - -EXPORT_SYMBOL_GPL(ipath_layer_send_hdr); - -int ipath_layer_set_piointbufavail_int(struct ipath_devdata *dd) -{ - set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl); - - ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, - dd->ipath_sendctrl); - return 0; -} - -EXPORT_SYMBOL_GPL(ipath_layer_set_piointbufavail_int); diff --git a/drivers/infiniband/hw/ipath/ipath_layer.h b/drivers/infiniband/hw/ipath/ipath_layer.h deleted file mode 100644 index 415709c4d85..00000000000 --- a/drivers/infiniband/hw/ipath/ipath_layer.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved. - * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _IPATH_LAYER_H -#define _IPATH_LAYER_H - -/* - * This header file is for symbols shared between the infinipath driver - * and drivers layered upon it (such as ipath). - */ - -struct sk_buff; -struct ipath_devdata; -struct ether_header; - -int ipath_layer_register(void *(*l_add)(int, struct ipath_devdata *), - void (*l_remove)(void *), - int (*l_intr)(void *, u32), - int (*l_rcv)(void *, void *, - struct sk_buff *), - u16 rcv_opcode, - int (*l_rcv_lid)(void *, void *)); -void ipath_layer_unregister(void); -int ipath_layer_open(struct ipath_devdata *, u32 * pktmax); -u16 ipath_layer_get_lid(struct ipath_devdata *dd); -int ipath_layer_get_mac(struct ipath_devdata *dd, u8 *); -u16 ipath_layer_get_bcast(struct ipath_devdata *dd); -int ipath_layer_send_hdr(struct ipath_devdata *dd, - struct ether_header *hdr); -int ipath_layer_set_piointbufavail_int(struct ipath_devdata *dd); - -/* ipath_ether interrupt values */ -#define IPATH_LAYER_INT_IF_UP 0x2 -#define IPATH_LAYER_INT_IF_DOWN 0x4 -#define IPATH_LAYER_INT_LID 0x8 -#define IPATH_LAYER_INT_SEND_CONTINUE 0x10 -#define IPATH_LAYER_INT_BCAST 0x40 - -extern unsigned ipath_debug; /* debugging bit mask */ - -#endif /* _IPATH_LAYER_H */ diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h index 9bbe81967f1..1a24c6a4a81 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.h +++ b/drivers/infiniband/hw/ipath/ipath_verbs.h @@ -42,8 +42,6 @@ #include <rdma/ib_pack.h> #include <rdma/ib_user_verbs.h> -#include "ipath_layer.h" - #define IPATH_MAX_RDMA_ATOMIC 4 #define QPN_MAX (1 << 24) diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index b5a24fbef70..f6315dfb213 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -415,9 +415,11 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, return 0; err_wrid: - if (pd->uobject && !init_attr->srq) - mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), &qp->db); - else { + if (pd->uobject) { + if (!init_attr->srq) + mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), + &qp->db); + } else { kfree(qp->sq.wrid); kfree(qp->rq.wrid); } @@ -742,7 +744,7 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, if (attr->path_mtu < IB_MTU_256 || attr->path_mtu > IB_MTU_4096) { printk(KERN_ERR "path MTU (%u) is invalid\n", attr->path_mtu); - return -EINVAL; + goto out; } context->mtu_msgmax = (attr->path_mtu << 5) | 31; } @@ -781,10 +783,8 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, if (attr_mask & IB_QP_AV) { if (mlx4_set_path(dev, &attr->ah_attr, &context->pri_path, - attr_mask & IB_QP_PORT ? attr->port_num : qp->port)) { - err = -EINVAL; + attr_mask & IB_QP_PORT ? attr->port_num : qp->port)) goto out; - } optpar |= (MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH | MLX4_QP_OPTPAR_SCHED_QUEUE); @@ -798,15 +798,15 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, if (attr_mask & IB_QP_ALT_PATH) { if (attr->alt_port_num == 0 || attr->alt_port_num > dev->dev->caps.num_ports) - return -EINVAL; + goto out; if (attr->alt_pkey_index >= dev->dev->caps.pkey_table_len[attr->alt_port_num]) - return -EINVAL; + goto out; if (mlx4_set_path(dev, &attr->alt_ah_attr, &context->alt_path, attr->alt_port_num)) - return -EINVAL; + goto out; context->alt_path.pkey_index = attr->alt_pkey_index; context->alt_path.ackto = attr->alt_timeout << 3; diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.c b/drivers/infiniband/hw/mthca/mthca_cmd.c index f40558d7647..acc95892713 100644 --- a/drivers/infiniband/hw/mthca/mthca_cmd.c +++ b/drivers/infiniband/hw/mthca/mthca_cmd.c @@ -357,8 +357,6 @@ void mthca_cmd_event(struct mthca_dev *dev, context->status = status; context->out_param = out_param; - context->token += dev->cmd.token_mask + 1; - complete(&context->done); } @@ -380,6 +378,7 @@ static int mthca_cmd_wait(struct mthca_dev *dev, spin_lock(&dev->cmd.context_lock); BUG_ON(dev->cmd.free_head < 0); context = &dev->cmd.context[dev->cmd.free_head]; + context->token += dev->cmd.token_mask + 1; dev->cmd.free_head = context->next; spin_unlock(&dev->cmd.context_lock); diff --git a/drivers/kvm/Kconfig b/drivers/kvm/Kconfig index 2f661e5f0da..6cecc396e04 100644 --- a/drivers/kvm/Kconfig +++ b/drivers/kvm/Kconfig @@ -11,6 +11,7 @@ if VIRTUALIZATION config KVM tristate "Kernel-based Virtual Machine (KVM) support" depends on X86 && EXPERIMENTAL + select ANON_INODES ---help--- Support hosting fully virtualized guest machines using hardware virtualization extensions. You will need a fairly recent diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 87d2046f866..4468cb3a8d2 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -1,9 +1,6 @@ - -menu "LED devices" - depends on HAS_IOMEM - -config NEW_LEDS +menuconfig NEW_LEDS bool "LED Support" + depends on HAS_IOMEM help Say Y to enable Linux LED support. This allows control of supported LEDs from both userspace and optionally, by kernel events (triggers). @@ -11,9 +8,10 @@ config NEW_LEDS This is not related to standard keyboard LEDs which are controlled via the input system. +if NEW_LEDS + config LEDS_CLASS tristate "LED Class Support" - depends on NEW_LEDS help This option enables the led sysfs class in /sys/class/leds. You'll need this to do anything useful with LEDs. If unsure, say N. @@ -95,11 +93,18 @@ config LEDS_COBALT help This option enables support for the front LED on Cobalt Server +config LEDS_GPIO + tristate "LED Support for GPIO connected LEDs" + depends on LEDS_CLASS && GENERIC_GPIO + help + This option enables support for the LEDs connected to GPIO + outputs. To be useful the particular board must have LEDs + and they must be connected to the GPIO lines. + comment "LED Triggers" config LEDS_TRIGGERS bool "LED Trigger support" - depends on NEW_LEDS help This option enables trigger support for the leds class. These triggers allow kernel events to drive the LEDs and can @@ -128,5 +133,4 @@ config LEDS_TRIGGER_HEARTBEAT load average. If unsure, say Y. -endmenu - +endif # NEW_LEDS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index aa2c18efa5b..f8995c9bc2e 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o obj-$(CONFIG_LEDS_H1940) += leds-h1940.o obj-$(CONFIG_LEDS_COBALT) += leds-cobalt.o +obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 3c1711210e3..4211293ce86 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -2,7 +2,7 @@ * LED Class Core * * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu> - * Copyright (C) 2005-2006 Richard Purdie <rpurdie@openedhand.com> + * Copyright (C) 2005-2007 Richard Purdie <rpurdie@openedhand.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 @@ -24,9 +24,10 @@ static struct class *leds_class; -static ssize_t led_brightness_show(struct class_device *dev, char *buf) +static ssize_t led_brightness_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct led_classdev *led_cdev = class_get_devdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); ssize_t ret = 0; /* no lock needed for this */ @@ -36,10 +37,10 @@ static ssize_t led_brightness_show(struct class_device *dev, char *buf) return ret; } -static ssize_t led_brightness_store(struct class_device *dev, - const char *buf, size_t size) +static ssize_t led_brightness_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) { - struct led_classdev *led_cdev = class_get_devdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); ssize_t ret = -EINVAL; char *after; unsigned long state = simple_strtoul(buf, &after, 10); @@ -56,10 +57,9 @@ static ssize_t led_brightness_store(struct class_device *dev, return ret; } -static CLASS_DEVICE_ATTR(brightness, 0644, led_brightness_show, - led_brightness_store); +static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store); #ifdef CONFIG_LEDS_TRIGGERS -static CLASS_DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); +static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); #endif /** @@ -93,16 +93,15 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) { int rc; - led_cdev->class_dev = class_device_create(leds_class, NULL, 0, - parent, "%s", led_cdev->name); - if (unlikely(IS_ERR(led_cdev->class_dev))) - return PTR_ERR(led_cdev->class_dev); + led_cdev->dev = device_create(leds_class, parent, 0, "%s", + led_cdev->name); + if (unlikely(IS_ERR(led_cdev->dev))) + return PTR_ERR(led_cdev->dev); - class_set_devdata(led_cdev->class_dev, led_cdev); + dev_set_drvdata(led_cdev->dev, led_cdev); /* register the attributes */ - rc = class_device_create_file(led_cdev->class_dev, - &class_device_attr_brightness); + rc = device_create_file(led_cdev->dev, &dev_attr_brightness); if (rc) goto err_out; @@ -114,8 +113,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) #ifdef CONFIG_LEDS_TRIGGERS rwlock_init(&led_cdev->trigger_lock); - rc = class_device_create_file(led_cdev->class_dev, - &class_device_attr_trigger); + rc = device_create_file(led_cdev->dev, &dev_attr_trigger); if (rc) goto err_out_led_list; @@ -123,18 +121,17 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) #endif printk(KERN_INFO "Registered led device: %s\n", - led_cdev->class_dev->class_id); + led_cdev->name); return 0; #ifdef CONFIG_LEDS_TRIGGERS err_out_led_list: - class_device_remove_file(led_cdev->class_dev, - &class_device_attr_brightness); + device_remove_file(led_cdev->dev, &dev_attr_brightness); list_del(&led_cdev->node); #endif err_out: - class_device_unregister(led_cdev->class_dev); + device_unregister(led_cdev->dev); return rc; } EXPORT_SYMBOL_GPL(led_classdev_register); @@ -147,18 +144,16 @@ EXPORT_SYMBOL_GPL(led_classdev_register); */ void led_classdev_unregister(struct led_classdev *led_cdev) { - class_device_remove_file(led_cdev->class_dev, - &class_device_attr_brightness); + device_remove_file(led_cdev->dev, &dev_attr_brightness); #ifdef CONFIG_LEDS_TRIGGERS - class_device_remove_file(led_cdev->class_dev, - &class_device_attr_trigger); + device_remove_file(led_cdev->dev, &dev_attr_trigger); write_lock(&led_cdev->trigger_lock); if (led_cdev->trigger) led_trigger_set(led_cdev, NULL); write_unlock(&led_cdev->trigger_lock); #endif - class_device_unregister(led_cdev->class_dev); + device_unregister(led_cdev->dev); write_lock(&leds_list_lock); list_del(&led_cdev->node); diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 454fb0901f8..575368c2b10 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -1,7 +1,7 @@ /* * LED Triggers Core * - * Copyright 2005-2006 Openedhand Ltd. + * Copyright 2005-2007 Openedhand Ltd. * * Author: Richard Purdie <rpurdie@openedhand.com> * @@ -28,10 +28,10 @@ static DEFINE_RWLOCK(triggers_list_lock); static LIST_HEAD(trigger_list); -ssize_t led_trigger_store(struct class_device *dev, const char *buf, - size_t count) +ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { - struct led_classdev *led_cdev = class_get_devdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); char trigger_name[TRIG_NAME_MAX]; struct led_trigger *trig; size_t len; @@ -67,9 +67,10 @@ ssize_t led_trigger_store(struct class_device *dev, const char *buf, } -ssize_t led_trigger_show(struct class_device *dev, char *buf) +ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr, + char *buf) { - struct led_classdev *led_cdev = class_get_devdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_trigger *trig; int len = 0; @@ -183,13 +184,20 @@ int led_trigger_register(struct led_trigger *trigger) void led_trigger_register_simple(const char *name, struct led_trigger **tp) { struct led_trigger *trigger; + int err; trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); if (trigger) { trigger->name = name; - led_trigger_register(trigger); - } + err = led_trigger_register(trigger); + if (err < 0) + printk(KERN_WARNING "LED trigger %s failed to register" + " (%d)\n", name, err); + } else + printk(KERN_WARNING "LED trigger %s failed to register" + " (no memory)\n", name); + *tp = trigger; } @@ -215,7 +223,8 @@ void led_trigger_unregister(struct led_trigger *trigger) void led_trigger_unregister_simple(struct led_trigger *trigger) { - led_trigger_unregister(trigger); + if (trigger) + led_trigger_unregister(trigger); kfree(trigger); } diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c new file mode 100644 index 00000000000..47d90db280c --- /dev/null +++ b/drivers/leds/leds-gpio.c @@ -0,0 +1,199 @@ +/* + * LEDs driver for GPIOs + * + * Copyright (C) 2007 8D Technologies inc. + * Raphael Assenat <raph@8d.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. + * + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/leds.h> +#include <linux/workqueue.h> + +#include <asm/gpio.h> + +struct gpio_led_data { + struct led_classdev cdev; + unsigned gpio; + struct work_struct work; + u8 new_level; + u8 can_sleep; + u8 active_low; +}; + +static void gpio_led_work(struct work_struct *work) +{ + struct gpio_led_data *led_dat = + container_of(work, struct gpio_led_data, work); + + gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level); +} + +static void gpio_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct gpio_led_data *led_dat = + container_of(led_cdev, struct gpio_led_data, cdev); + int level; + + if (value == LED_OFF) + level = 0; + else + level = 1; + + if (led_dat->active_low) + level = !level; + + /* setting GPIOs with I2C/etc requires a preemptible task context */ + if (led_dat->can_sleep) { + if (preempt_count()) { + led_dat->new_level = level; + schedule_work(&led_dat->work); + } else + gpio_set_value_cansleep(led_dat->gpio, level); + } else + gpio_set_value(led_dat->gpio, level); +} + +static int __init gpio_led_probe(struct platform_device *pdev) +{ + struct gpio_led_platform_data *pdata = pdev->dev.platform_data; + struct gpio_led *cur_led; + struct gpio_led_data *leds_data, *led_dat; + int i, ret = 0; + + if (!pdata) + return -EBUSY; + + leds_data = kzalloc(sizeof(struct gpio_led_data) * pdata->num_leds, + GFP_KERNEL); + if (!leds_data) + return -ENOMEM; + + for (i = 0; i < pdata->num_leds; i++) { + cur_led = &pdata->leds[i]; + led_dat = &leds_data[i]; + + led_dat->cdev.name = cur_led->name; + led_dat->cdev.default_trigger = cur_led->default_trigger; + led_dat->gpio = cur_led->gpio; + led_dat->can_sleep = gpio_cansleep(cur_led->gpio); + led_dat->active_low = cur_led->active_low; + led_dat->cdev.brightness_set = gpio_led_set; + led_dat->cdev.brightness = cur_led->active_low ? LED_FULL : LED_OFF; + + ret = gpio_request(led_dat->gpio, led_dat->cdev.name); + if (ret < 0) + goto err; + + gpio_direction_output(led_dat->gpio, led_dat->active_low); + + ret = led_classdev_register(&pdev->dev, &led_dat->cdev); + if (ret < 0) { + gpio_free(led_dat->gpio); + goto err; + } + + INIT_WORK(&led_dat->work, gpio_led_work); + } + + platform_set_drvdata(pdev, leds_data); + + return 0; + +err: + if (i > 0) { + for (i = i - 1; i >= 0; i--) { + led_classdev_unregister(&leds_data[i].cdev); + gpio_free(leds_data[i].gpio); + } + } + + flush_scheduled_work(); + kfree(leds_data); + + return ret; +} + +static int __exit gpio_led_remove(struct platform_device *pdev) +{ + int i; + struct gpio_led_platform_data *pdata = pdev->dev.platform_data; + struct gpio_led_data *leds_data; + + leds_data = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->num_leds; i++) { + led_classdev_unregister(&leds_data[i].cdev); + gpio_free(leds_data[i].gpio); + } + + kfree(leds_data); + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_led_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct gpio_led_platform_data *pdata = pdev->dev.platform_data; + struct gpio_led_data *leds_data; + int i; + + leds_data = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->num_leds; i++) + led_classdev_suspend(&leds_data[i].cdev); + + return 0; +} + +static int gpio_led_resume(struct platform_device *pdev) +{ + struct gpio_led_platform_data *pdata = pdev->dev.platform_data; + struct gpio_led_data *leds_data; + int i; + + leds_data = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->num_leds; i++) + led_classdev_resume(&leds_data[i].cdev); + + return 0; +} +#else +#define gpio_led_suspend NULL +#define gpio_led_resume NULL +#endif + +static struct platform_driver gpio_led_driver = { + .remove = __exit_p(gpio_led_remove), + .suspend = gpio_led_suspend, + .resume = gpio_led_resume, + .driver = { + .name = "leds-gpio", + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_led_init(void) +{ + return platform_driver_probe(&gpio_led_driver, gpio_led_probe); +} + +static void __exit gpio_led_exit(void) +{ + platform_driver_unregister(&gpio_led_driver); +} + +module_init(gpio_led_init); +module_exit(gpio_led_exit); + +MODULE_AUTHOR("Raphael Assenat <raph@8d.com>"); +MODULE_DESCRIPTION("GPIO LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-locomo.c b/drivers/leds/leds-locomo.c index 6f2d449ba98..bfac499f325 100644 --- a/drivers/leds/leds-locomo.c +++ b/drivers/leds/leds-locomo.c @@ -19,7 +19,7 @@ static void locomoled_brightness_set(struct led_classdev *led_cdev, enum led_brightness value, int offset) { - struct locomo_dev *locomo_dev = LOCOMO_DEV(led_cdev->class_dev->dev); + struct locomo_dev *locomo_dev = LOCOMO_DEV(led_cdev->dev); unsigned long flags; local_irq_save(flags); diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index a715c4ed93f..f2f3884fe06 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -13,6 +13,7 @@ #ifndef __LEDS_H_INCLUDED #define __LEDS_H_INCLUDED +#include <linux/device.h> #include <linux/leds.h> static inline void led_set_brightness(struct led_classdev *led_cdev, @@ -37,8 +38,9 @@ void led_trigger_set(struct led_classdev *led_cdev, #define led_trigger_set(x, y) do {} while(0) #endif -ssize_t led_trigger_store(struct class_device *dev, const char *buf, - size_t count); -ssize_t led_trigger_show(struct class_device *dev, char *buf); +ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr, + char *buf); #endif /* __LEDS_H_INCLUDED */ diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c index d756bdb01c5..ed9ff02c77e 100644 --- a/drivers/leds/ledtrig-timer.c +++ b/drivers/leds/ledtrig-timer.c @@ -52,9 +52,10 @@ static void led_timer_function(unsigned long data) mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay)); } -static ssize_t led_delay_on_show(struct class_device *dev, char *buf) +static ssize_t led_delay_on_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct led_classdev *led_cdev = class_get_devdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); struct timer_trig_data *timer_data = led_cdev->trigger_data; sprintf(buf, "%lu\n", timer_data->delay_on); @@ -62,10 +63,10 @@ static ssize_t led_delay_on_show(struct class_device *dev, char *buf) return strlen(buf) + 1; } -static ssize_t led_delay_on_store(struct class_device *dev, const char *buf, - size_t size) +static ssize_t led_delay_on_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) { - struct led_classdev *led_cdev = class_get_devdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); struct timer_trig_data *timer_data = led_cdev->trigger_data; int ret = -EINVAL; char *after; @@ -84,9 +85,10 @@ static ssize_t led_delay_on_store(struct class_device *dev, const char *buf, return ret; } -static ssize_t led_delay_off_show(struct class_device *dev, char *buf) +static ssize_t led_delay_off_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct led_classdev *led_cdev = class_get_devdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); struct timer_trig_data *timer_data = led_cdev->trigger_data; sprintf(buf, "%lu\n", timer_data->delay_off); @@ -94,10 +96,10 @@ static ssize_t led_delay_off_show(struct class_device *dev, char *buf) return strlen(buf) + 1; } -static ssize_t led_delay_off_store(struct class_device *dev, const char *buf, - size_t size) +static ssize_t led_delay_off_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) { - struct led_classdev *led_cdev = class_get_devdata(dev); + struct led_classdev *led_cdev = dev_get_drvdata(dev); struct timer_trig_data *timer_data = led_cdev->trigger_data; int ret = -EINVAL; char *after; @@ -116,10 +118,8 @@ static ssize_t led_delay_off_store(struct class_device *dev, const char *buf, return ret; } -static CLASS_DEVICE_ATTR(delay_on, 0644, led_delay_on_show, - led_delay_on_store); -static CLASS_DEVICE_ATTR(delay_off, 0644, led_delay_off_show, - led_delay_off_store); +static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store); +static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store); static void timer_trig_activate(struct led_classdev *led_cdev) { @@ -136,18 +136,17 @@ static void timer_trig_activate(struct led_classdev *led_cdev) timer_data->timer.function = led_timer_function; timer_data->timer.data = (unsigned long) led_cdev; - rc = class_device_create_file(led_cdev->class_dev, - &class_device_attr_delay_on); - if (rc) goto err_out; - rc = class_device_create_file(led_cdev->class_dev, - &class_device_attr_delay_off); - if (rc) goto err_out_delayon; + rc = device_create_file(led_cdev->dev, &dev_attr_delay_on); + if (rc) + goto err_out; + rc = device_create_file(led_cdev->dev, &dev_attr_delay_off); + if (rc) + goto err_out_delayon; return; err_out_delayon: - class_device_remove_file(led_cdev->class_dev, - &class_device_attr_delay_on); + device_remove_file(led_cdev->dev, &dev_attr_delay_on); err_out: led_cdev->trigger_data = NULL; kfree(timer_data); @@ -158,10 +157,8 @@ static void timer_trig_deactivate(struct led_classdev *led_cdev) struct timer_trig_data *timer_data = led_cdev->trigger_data; if (timer_data) { - class_device_remove_file(led_cdev->class_dev, - &class_device_attr_delay_on); - class_device_remove_file(led_cdev->class_dev, - &class_device_attr_delay_off); + device_remove_file(led_cdev->dev, &dev_attr_delay_on); + device_remove_file(led_cdev->dev, &dev_attr_delay_off); del_timer_sync(&timer_data->timer); kfree(timer_data); } diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c index 4177ff00475..2c21d4f25cc 100644 --- a/drivers/macintosh/rack-meter.c +++ b/drivers/macintosh/rack-meter.c @@ -30,7 +30,6 @@ #include <asm/machdep.h> #include <asm/pmac_feature.h> #include <asm/dbdma.h> -#include <asm/dbdma.h> #include <asm/macio.h> #include <asm/keylargo.h> diff --git a/drivers/message/fusion/Kconfig b/drivers/message/fusion/Kconfig index c88cc75ab49..4494e0fd36c 100644 --- a/drivers/message/fusion/Kconfig +++ b/drivers/message/fusion/Kconfig @@ -37,6 +37,7 @@ config FUSION_FC LSIFC929 LSIFC929X LSIFC929XL + Brocade FC 410/420 config FUSION_SAS tristate "Fusion MPT ScsiHost drivers for SAS" diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 5a10c87239c..04f75e24dce 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -161,6 +161,7 @@ static int mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum); static void mpt_read_ioc_pg_1(MPT_ADAPTER *ioc); static void mpt_read_ioc_pg_4(MPT_ADAPTER *ioc); static void mpt_timer_expired(unsigned long data); +static void mpt_get_manufacturing_pg_0(MPT_ADAPTER *ioc); static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch); static int SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp); static int mpt_host_page_access_control(MPT_ADAPTER *ioc, u8 access_control_value, int sleepFlag); @@ -1131,6 +1132,248 @@ mpt_verify_adapter(int iocid, MPT_ADAPTER **iocpp) return -1; } +/** + * mpt_get_product_name - returns product string + * @vendor: pci vendor id + * @device: pci device id + * @revision: pci revision id + * @prod_name: string returned + * + * Returns product string displayed when driver loads, + * in /proc/mpt/summary and /sysfs/class/scsi_host/host<X>/version_product + * + **/ +static void +mpt_get_product_name(u16 vendor, u16 device, u8 revision, char *prod_name) +{ + char *product_str = NULL; + + if (vendor == PCI_VENDOR_ID_BROCADE) { + switch (device) + { + case MPI_MANUFACTPAGE_DEVICEID_FC949E: + switch (revision) + { + case 0x00: + product_str = "BRE040 A0"; + break; + case 0x01: + product_str = "BRE040 A1"; + break; + default: + product_str = "BRE040"; + break; + } + break; + } + goto out; + } + + switch (device) + { + case MPI_MANUFACTPAGE_DEVICEID_FC909: + product_str = "LSIFC909 B1"; + break; + case MPI_MANUFACTPAGE_DEVICEID_FC919: + product_str = "LSIFC919 B0"; + break; + case MPI_MANUFACTPAGE_DEVICEID_FC929: + product_str = "LSIFC929 B0"; + break; + case MPI_MANUFACTPAGE_DEVICEID_FC919X: + if (revision < 0x80) + product_str = "LSIFC919X A0"; + else + product_str = "LSIFC919XL A1"; + break; + case MPI_MANUFACTPAGE_DEVICEID_FC929X: + if (revision < 0x80) + product_str = "LSIFC929X A0"; + else + product_str = "LSIFC929XL A1"; + break; + case MPI_MANUFACTPAGE_DEVICEID_FC939X: + product_str = "LSIFC939X A1"; + break; + case MPI_MANUFACTPAGE_DEVICEID_FC949X: + product_str = "LSIFC949X A1"; + break; + case MPI_MANUFACTPAGE_DEVICEID_FC949E: + switch (revision) + { + case 0x00: + product_str = "LSIFC949E A0"; + break; + case 0x01: + product_str = "LSIFC949E A1"; + break; + default: + product_str = "LSIFC949E"; + break; + } + break; + case MPI_MANUFACTPAGE_DEVID_53C1030: + switch (revision) + { + case 0x00: + product_str = "LSI53C1030 A0"; + break; + case 0x01: + product_str = "LSI53C1030 B0"; + break; + case 0x03: + product_str = "LSI53C1030 B1"; + break; + case 0x07: + product_str = "LSI53C1030 B2"; + break; + case 0x08: + product_str = "LSI53C1030 C0"; + break; + case 0x80: + product_str = "LSI53C1030T A0"; + break; + case 0x83: + product_str = "LSI53C1030T A2"; + break; + case 0x87: + product_str = "LSI53C1030T A3"; + break; + case 0xc1: + product_str = "LSI53C1020A A1"; + break; + default: + product_str = "LSI53C1030"; + break; + } + break; + case MPI_MANUFACTPAGE_DEVID_1030_53C1035: + switch (revision) + { + case 0x03: + product_str = "LSI53C1035 A2"; + break; + case 0x04: + product_str = "LSI53C1035 B0"; + break; + default: + product_str = "LSI53C1035"; + break; + } + break; + case MPI_MANUFACTPAGE_DEVID_SAS1064: + switch (revision) + { + case 0x00: + product_str = "LSISAS1064 A1"; + break; + case 0x01: + product_str = "LSISAS1064 A2"; + break; + case 0x02: + product_str = "LSISAS1064 A3"; + break; + case 0x03: + product_str = "LSISAS1064 A4"; + break; + default: + product_str = "LSISAS1064"; + break; + } + break; + case MPI_MANUFACTPAGE_DEVID_SAS1064E: + switch (revision) + { + case 0x00: + product_str = "LSISAS1064E A0"; + break; + case 0x01: + product_str = "LSISAS1064E B0"; + break; + case 0x02: + product_str = "LSISAS1064E B1"; + break; + case 0x04: + product_str = "LSISAS1064E B2"; + break; + case 0x08: + product_str = "LSISAS1064E B3"; + break; + default: + product_str = "LSISAS1064E"; + break; + } + break; + case MPI_MANUFACTPAGE_DEVID_SAS1068: + switch (revision) + { + case 0x00: + product_str = "LSISAS1068 A0"; + break; + case 0x01: + product_str = "LSISAS1068 B0"; + break; + case 0x02: + product_str = "LSISAS1068 B1"; + break; + default: + product_str = "LSISAS1068"; + break; + } + break; + case MPI_MANUFACTPAGE_DEVID_SAS1068E: + switch (revision) + { + case 0x00: + product_str = "LSISAS1068E A0"; + break; + case 0x01: + product_str = "LSISAS1068E B0"; + break; + case 0x02: + product_str = "LSISAS1068E B1"; + break; + case 0x04: + product_str = "LSISAS1068E B2"; + break; + case 0x08: + product_str = "LSISAS1068E B3"; + break; + default: + product_str = "LSISAS1068E"; + break; + } + break; + case MPI_MANUFACTPAGE_DEVID_SAS1078: + switch (revision) + { + case 0x00: + product_str = "LSISAS1078 A0"; + break; + case 0x01: + product_str = "LSISAS1078 B0"; + break; + case 0x02: + product_str = "LSISAS1078 C0"; + break; + case 0x03: + product_str = "LSISAS1078 C1"; + break; + case 0x04: + product_str = "LSISAS1078 C2"; + break; + default: + product_str = "LSISAS1078"; + break; + } + break; + } + + out: + if (product_str) + sprintf(prod_name, "%s", product_str); +} + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** * mpt_attach - Install a PCI intelligent MPT adapter. @@ -1274,23 +1517,23 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) ioc->pio_chip = (SYSIF_REGS __iomem *)pmem; } - if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC909) { - ioc->prod_name = "LSIFC909"; - ioc->bus_type = FC; - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC929) { - ioc->prod_name = "LSIFC929"; - ioc->bus_type = FC; - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC919) { - ioc->prod_name = "LSIFC919"; - ioc->bus_type = FC; - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC929X) { - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); + mpt_get_product_name(pdev->vendor, pdev->device, revision, ioc->prod_name); + + switch (pdev->device) + { + case MPI_MANUFACTPAGE_DEVICEID_FC939X: + case MPI_MANUFACTPAGE_DEVICEID_FC949X: + ioc->errata_flag_1064 = 1; + case MPI_MANUFACTPAGE_DEVICEID_FC909: + case MPI_MANUFACTPAGE_DEVICEID_FC929: + case MPI_MANUFACTPAGE_DEVICEID_FC919: + case MPI_MANUFACTPAGE_DEVICEID_FC949E: ioc->bus_type = FC; + break; + + case MPI_MANUFACTPAGE_DEVICEID_FC929X: if (revision < XL_929) { - ioc->prod_name = "LSIFC929X"; /* 929X Chip Fix. Set Split transactions level * for PCIX. Set MOST bits to zero. */ @@ -1298,75 +1541,46 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) pcixcmd &= 0x8F; pci_write_config_byte(pdev, 0x6a, pcixcmd); } else { - ioc->prod_name = "LSIFC929XL"; /* 929XL Chip Fix. Set MMRBC to 0x08. */ pci_read_config_byte(pdev, 0x6a, &pcixcmd); pcixcmd |= 0x08; pci_write_config_byte(pdev, 0x6a, pcixcmd); } - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC919X) { - ioc->prod_name = "LSIFC919X"; ioc->bus_type = FC; + break; + + case MPI_MANUFACTPAGE_DEVICEID_FC919X: /* 919X Chip Fix. Set Split transactions level * for PCIX. Set MOST bits to zero. */ pci_read_config_byte(pdev, 0x6a, &pcixcmd); pcixcmd &= 0x8F; pci_write_config_byte(pdev, 0x6a, pcixcmd); - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC939X) { - ioc->prod_name = "LSIFC939X"; - ioc->bus_type = FC; - ioc->errata_flag_1064 = 1; - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC949X) { - ioc->prod_name = "LSIFC949X"; ioc->bus_type = FC; - ioc->errata_flag_1064 = 1; - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC949E) { - ioc->prod_name = "LSIFC949E"; - ioc->bus_type = FC; - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVID_53C1030) { - ioc->prod_name = "LSI53C1030"; - ioc->bus_type = SPI; + break; + + case MPI_MANUFACTPAGE_DEVID_53C1030: /* 1030 Chip Fix. Disable Split transactions * for PCIX. Set MOST bits to zero if Rev < C0( = 8). */ - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); if (revision < C0_1030) { pci_read_config_byte(pdev, 0x6a, &pcixcmd); pcixcmd &= 0x8F; pci_write_config_byte(pdev, 0x6a, pcixcmd); } - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVID_1030_53C1035) { - ioc->prod_name = "LSI53C1035"; + + case MPI_MANUFACTPAGE_DEVID_1030_53C1035: ioc->bus_type = SPI; - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1064) { - ioc->prod_name = "LSISAS1064"; - ioc->bus_type = SAS; - ioc->errata_flag_1064 = 1; - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1068) { - ioc->prod_name = "LSISAS1068"; - ioc->bus_type = SAS; + break; + + case MPI_MANUFACTPAGE_DEVID_SAS1064: + case MPI_MANUFACTPAGE_DEVID_SAS1068: ioc->errata_flag_1064 = 1; - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1064E) { - ioc->prod_name = "LSISAS1064E"; - ioc->bus_type = SAS; - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1068E) { - ioc->prod_name = "LSISAS1068E"; - ioc->bus_type = SAS; - } - else if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1078) { - ioc->prod_name = "LSISAS1078"; + + case MPI_MANUFACTPAGE_DEVID_SAS1064E: + case MPI_MANUFACTPAGE_DEVID_SAS1068E: + case MPI_MANUFACTPAGE_DEVID_SAS1078: ioc->bus_type = SAS; } @@ -1880,6 +2094,7 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag) } GetIoUnitPage2(ioc); + mpt_get_manufacturing_pg_0(ioc); } /* @@ -2138,8 +2353,8 @@ MptDisplayIocCapabilities(MPT_ADAPTER *ioc) int i = 0; printk(KERN_INFO "%s: ", ioc->name); - if (ioc->prod_name && strlen(ioc->prod_name) > 3) - printk("%s: ", ioc->prod_name+3); + if (ioc->prod_name) + printk("%s: ", ioc->prod_name); printk("Capabilities={"); if (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR) { @@ -5190,6 +5405,49 @@ mpt_read_ioc_pg_1(MPT_ADAPTER *ioc) return; } +static void +mpt_get_manufacturing_pg_0(MPT_ADAPTER *ioc) +{ + CONFIGPARMS cfg; + ConfigPageHeader_t hdr; + dma_addr_t buf_dma; + ManufacturingPage0_t *pbuf = NULL; + + memset(&cfg, 0 , sizeof(CONFIGPARMS)); + memset(&hdr, 0 , sizeof(ConfigPageHeader_t)); + + hdr.PageType = MPI_CONFIG_PAGETYPE_MANUFACTURING; + cfg.cfghdr.hdr = &hdr; + cfg.physAddr = -1; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.timeout = 10; + + if (mpt_config(ioc, &cfg) != 0) + goto out; + + if (!cfg.cfghdr.hdr->PageLength) + goto out; + + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + pbuf = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4, &buf_dma); + if (!pbuf) + goto out; + + cfg.physAddr = buf_dma; + + if (mpt_config(ioc, &cfg) != 0) + goto out; + + memcpy(ioc->board_name, pbuf->BoardName, sizeof(ioc->board_name)); + memcpy(ioc->board_assembly, pbuf->BoardAssembly, sizeof(ioc->board_assembly)); + memcpy(ioc->board_tracer, pbuf->BoardTracerNumber, sizeof(ioc->board_tracer)); + + out: + + if (pbuf) + pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, pbuf, buf_dma); +} + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** * SendEventNotification - Send EventNotification (on or off) request to adapter diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index 05eb6e52875..98eb9c688e1 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -537,7 +537,14 @@ typedef struct _MPT_ADAPTER int id; /* Unique adapter id N {0,1,2,...} */ int pci_irq; /* This irq */ char name[MPT_NAME_LENGTH]; /* "iocN" */ - char *prod_name; /* "LSIFC9x9" */ + char prod_name[MPT_NAME_LENGTH]; /* "LSIFC9x9" */ + char board_name[16]; + char board_assembly[16]; + char board_tracer[16]; + u16 nvdata_version_persistent; + u16 nvdata_version_default; + u8 io_missing_delay; + u8 device_missing_delay; SYSIF_REGS __iomem *chip; /* == c8817000 (mmap) */ SYSIF_REGS __iomem *pio_chip; /* Programmed IO (downloadboot) */ u8 bus_type; diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index b766445f19a..f2ebaa9992f 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -130,6 +130,7 @@ static struct scsi_host_template mptfc_driver_template = { .max_sectors = 8192, .cmd_per_lun = 7, .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = mptscsih_host_attrs, }; /**************************************************************************** @@ -153,6 +154,8 @@ static struct pci_device_id mptfc_pci_table[] = { PCI_ANY_ID, PCI_ANY_ID }, { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVICEID_FC949E, PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_BROCADE, MPI_MANUFACTPAGE_DEVICEID_FC949E, + PCI_ANY_ID, PCI_ANY_ID }, {0} /* Terminating entry */ }; MODULE_DEVICE_TABLE(pci, mptfc_pci_table); diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 9e5424e1871..d5066464051 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -1119,6 +1119,7 @@ static struct scsi_host_template mptsas_driver_template = { .max_sectors = 8192, .cmd_per_lun = 7, .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = mptscsih_host_attrs, }; static int mptsas_get_linkerrors(struct sas_phy *phy) @@ -1390,6 +1391,11 @@ mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) goto out_free_consistent; } + ioc->nvdata_version_persistent = + le16_to_cpu(buffer->NvdataVersionPersistent); + ioc->nvdata_version_default = + le16_to_cpu(buffer->NvdataVersionDefault); + for (i = 0; i < port_info->num_phys; i++) { mptsas_print_phy_data(&buffer->PhyData[i]); port_info->phy_info[i].phy_id = i; @@ -1410,6 +1416,63 @@ mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) } static int +mptsas_sas_io_unit_pg1(MPT_ADAPTER *ioc) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasIOUnitPage1_t *buffer; + dma_addr_t dma_handle; + int error; + u16 device_missing_delay; + + memset(&hdr, 0, sizeof(ConfigExtendedPageHeader_t)); + memset(&cfg, 0, sizeof(CONFIGPARMS)); + + cfg.cfghdr.ehdr = &hdr; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.timeout = 10; + cfg.cfghdr.ehdr->PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + cfg.cfghdr.ehdr->ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; + cfg.cfghdr.ehdr->PageVersion = MPI_SASIOUNITPAGE1_PAGEVERSION; + cfg.cfghdr.ehdr->PageNumber = 1; + + error = mpt_config(ioc, &cfg); + if (error) + goto out; + if (!hdr.ExtPageLength) { + error = -ENXIO; + goto out; + } + + buffer = pci_alloc_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + &dma_handle); + if (!buffer) { + error = -ENOMEM; + goto out; + } + + cfg.physAddr = dma_handle; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + error = mpt_config(ioc, &cfg); + if (error) + goto out_free_consistent; + + ioc->io_missing_delay = + le16_to_cpu(buffer->IODeviceMissingDelay); + device_missing_delay = le16_to_cpu(buffer->ReportDeviceMissingDelay); + ioc->device_missing_delay = (device_missing_delay & MPI_SAS_IOUNIT1_REPORT_MISSING_UNIT_16) ? + (device_missing_delay & MPI_SAS_IOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16 : + device_missing_delay & MPI_SAS_IOUNIT1_REPORT_MISSING_TIMEOUT_MASK; + + out_free_consistent: + pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + buffer, dma_handle); + out: + return error; +} + +static int mptsas_sas_phy_pg0(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, u32 form, u32 form_specific) { @@ -1990,6 +2053,7 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc) if (error) goto out_free_port_info; + mptsas_sas_io_unit_pg1(ioc); mutex_lock(&ioc->sas_topology_mutex); ioc->handle = hba->phy_info[0].handle; port_info = mptsas_find_portinfo_by_handle(ioc, ioc->handle); @@ -3237,6 +3301,8 @@ static struct pci_driver mptsas_driver = { static int __init mptsas_init(void) { + int error; + show_mptmod_ver(my_NAME, my_VERSION); mptsas_transport_template = @@ -3260,7 +3326,11 @@ mptsas_init(void) ": Registered for IOC reset notifications\n")); } - return pci_register_driver(&mptsas_driver); + error = pci_register_driver(&mptsas_driver); + if (error) + sas_release_transport(mptsas_transport_template); + + return error; } static void __exit diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index d35617376f8..fd3aa2619f4 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -3187,6 +3187,159 @@ mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice) mptscsih_do_cmd(hd, &iocmd); } +static ssize_t +mptscsih_version_fw_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + MPT_ADAPTER *ioc = hd->ioc; + + return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", + (ioc->facts.FWVersion.Word & 0xFF000000) >> 24, + (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, + (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, + ioc->facts.FWVersion.Word & 0x000000FF); +} +static CLASS_DEVICE_ATTR(version_fw, S_IRUGO, mptscsih_version_fw_show, NULL); + +static ssize_t +mptscsih_version_bios_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + MPT_ADAPTER *ioc = hd->ioc; + + return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x\n", + (ioc->biosVersion & 0xFF000000) >> 24, + (ioc->biosVersion & 0x00FF0000) >> 16, + (ioc->biosVersion & 0x0000FF00) >> 8, + ioc->biosVersion & 0x000000FF); +} +static CLASS_DEVICE_ATTR(version_bios, S_IRUGO, mptscsih_version_bios_show, NULL); + +static ssize_t +mptscsih_version_mpi_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + MPT_ADAPTER *ioc = hd->ioc; + + return snprintf(buf, PAGE_SIZE, "%03x\n", ioc->facts.MsgVersion); +} +static CLASS_DEVICE_ATTR(version_mpi, S_IRUGO, mptscsih_version_mpi_show, NULL); + +static ssize_t +mptscsih_version_product_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + MPT_ADAPTER *ioc = hd->ioc; + + return snprintf(buf, PAGE_SIZE, "%s\n", ioc->prod_name); +} +static CLASS_DEVICE_ATTR(version_product, S_IRUGO, + mptscsih_version_product_show, NULL); + +static ssize_t +mptscsih_version_nvdata_persistent_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + MPT_ADAPTER *ioc = hd->ioc; + + return snprintf(buf, PAGE_SIZE, "%02xh\n", + ioc->nvdata_version_persistent); +} +static CLASS_DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, + mptscsih_version_nvdata_persistent_show, NULL); + +static ssize_t +mptscsih_version_nvdata_default_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + MPT_ADAPTER *ioc = hd->ioc; + + return snprintf(buf, PAGE_SIZE, "%02xh\n",ioc->nvdata_version_default); +} +static CLASS_DEVICE_ATTR(version_nvdata_default, S_IRUGO, + mptscsih_version_nvdata_default_show, NULL); + +static ssize_t +mptscsih_board_name_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + MPT_ADAPTER *ioc = hd->ioc; + + return snprintf(buf, PAGE_SIZE, "%s\n", ioc->board_name); +} +static CLASS_DEVICE_ATTR(board_name, S_IRUGO, mptscsih_board_name_show, NULL); + +static ssize_t +mptscsih_board_assembly_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + MPT_ADAPTER *ioc = hd->ioc; + + return snprintf(buf, PAGE_SIZE, "%s\n", ioc->board_assembly); +} +static CLASS_DEVICE_ATTR(board_assembly, S_IRUGO, + mptscsih_board_assembly_show, NULL); + +static ssize_t +mptscsih_board_tracer_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + MPT_ADAPTER *ioc = hd->ioc; + + return snprintf(buf, PAGE_SIZE, "%s\n", ioc->board_tracer); +} +static CLASS_DEVICE_ATTR(board_tracer, S_IRUGO, + mptscsih_board_tracer_show, NULL); + +static ssize_t +mptscsih_io_delay_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + MPT_ADAPTER *ioc = hd->ioc; + + return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay); +} +static CLASS_DEVICE_ATTR(io_delay, S_IRUGO, + mptscsih_io_delay_show, NULL); + +static ssize_t +mptscsih_device_delay_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + MPT_ADAPTER *ioc = hd->ioc; + + return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay); +} +static CLASS_DEVICE_ATTR(device_delay, S_IRUGO, + mptscsih_device_delay_show, NULL); + +struct class_device_attribute *mptscsih_host_attrs[] = { + &class_device_attr_version_fw, + &class_device_attr_version_bios, + &class_device_attr_version_mpi, + &class_device_attr_version_product, + &class_device_attr_version_nvdata_persistent, + &class_device_attr_version_nvdata_default, + &class_device_attr_board_name, + &class_device_attr_board_assembly, + &class_device_attr_board_tracer, + &class_device_attr_io_delay, + &class_device_attr_device_delay, + NULL, +}; +EXPORT_SYMBOL(mptscsih_host_attrs); + EXPORT_SYMBOL(mptscsih_remove); EXPORT_SYMBOL(mptscsih_shutdown); #ifdef CONFIG_PM diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h index 8eccdfe5701..67b088db2f1 100644 --- a/drivers/message/fusion/mptscsih.h +++ b/drivers/message/fusion/mptscsih.h @@ -129,3 +129,4 @@ extern void mptscsih_timer_expired(unsigned long data); extern int mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout); extern u8 mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id); extern int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id); +extern struct class_device_attribute *mptscsih_host_attrs[]; diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c index 6b3e0c00952..947fe290180 100644 --- a/drivers/message/fusion/mptspi.c +++ b/drivers/message/fusion/mptspi.c @@ -821,6 +821,7 @@ static struct scsi_host_template mptspi_driver_template = { .max_sectors = 8192, .cmd_per_lun = 7, .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = mptscsih_host_attrs, }; static int mptspi_write_spi_device_pg1(struct scsi_target *starget, @@ -1523,6 +1524,8 @@ static struct pci_driver mptspi_driver = { static int __init mptspi_init(void) { + int error; + show_mptmod_ver(my_NAME, my_VERSION); mptspi_transport_template = spi_attach_transport(&mptspi_transport_functions); @@ -1543,7 +1546,11 @@ mptspi_init(void) ": Registered for IOC reset notifications\n")); } - return pci_register_driver(&mptspi_driver); + error = pci_register_driver(&mptspi_driver); + if (error) + spi_release_transport(mptspi_transport_template); + + return error; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 1d516f24ba5..aaaa61ea421 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -150,6 +150,7 @@ config THINKPAD_ACPI depends on X86 && ACPI select BACKLIGHT_CLASS_DEVICE select HWMON + select NVRAM ---help--- This is a driver for the IBM and Lenovo ThinkPad laptops. It adds support for Fn-Fx key combinations, Bluetooth control, video @@ -196,4 +197,17 @@ config THINKPAD_ACPI_BAY If you are not sure, say Y here. +config THINKPAD_ACPI_INPUT_ENABLED + bool "Enable input layer support by default" + depends on THINKPAD_ACPI + default y + ---help--- + Enables hot key handling over the input layer by default. If unset, + the driver does not enable any hot key handling by default, and also + starts up with a mostly empty keymap. + + If you are not sure, say Y here. Say N to retain the deprecated + behavior of ibm-acpi, and thinkpad-acpi for kernels up to 2.6.21. + + endif # MISC_DEVICES diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index f7530605997..6b89854bd3f 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c @@ -1067,19 +1067,16 @@ static void asus_backlight_exit(void) } #define ASUS_LED_UNREGISTER(object) \ - if(object##_led.class_dev \ - && !IS_ERR(object##_led.class_dev)) \ - led_classdev_unregister(&object##_led) + led_classdev_unregister(&object##_led) static void asus_led_exit(void) { + destroy_workqueue(led_workqueue); ASUS_LED_UNREGISTER(mled); ASUS_LED_UNREGISTER(tled); ASUS_LED_UNREGISTER(pled); ASUS_LED_UNREGISTER(rled); ASUS_LED_UNREGISTER(gled); - - destroy_workqueue(led_workqueue); } static void __exit asus_laptop_exit(void) @@ -1135,29 +1132,42 @@ static int asus_led_init(struct device *dev) rv = ASUS_LED_REGISTER(mled, dev); if (rv) - return rv; + goto out; rv = ASUS_LED_REGISTER(tled, dev); if (rv) - return rv; + goto out1; rv = ASUS_LED_REGISTER(rled, dev); if (rv) - return rv; + goto out2; rv = ASUS_LED_REGISTER(pled, dev); if (rv) - return rv; + goto out3; rv = ASUS_LED_REGISTER(gled, dev); if (rv) - return rv; + goto out4; led_workqueue = create_singlethread_workqueue("led_workqueue"); if (!led_workqueue) - return -ENOMEM; + goto out5; return 0; +out5: + rv = -ENOMEM; + ASUS_LED_UNREGISTER(gled); +out4: + ASUS_LED_UNREGISTER(pled); +out3: + ASUS_LED_UNREGISTER(rled); +out2: + ASUS_LED_UNREGISTER(tled); +out1: + ASUS_LED_UNREGISTER(mled); +out: + return rv; } static int __init asus_laptop_init(void) diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index 9623eaf4f89..303e48ca0e8 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c @@ -142,43 +142,124 @@ struct sony_laptop_keypress { int key; }; -/* Correspondance table between sonypi events and input layer events */ -static struct { - int sonypiev; - int inputev; -} sony_laptop_inputkeys[] = { - { SONYPI_EVENT_CAPTURE_PRESSED, KEY_CAMERA }, - { SONYPI_EVENT_FNKEY_ONLY, KEY_FN }, - { SONYPI_EVENT_FNKEY_ESC, KEY_FN_ESC }, - { SONYPI_EVENT_FNKEY_F1, KEY_FN_F1 }, - { SONYPI_EVENT_FNKEY_F2, KEY_FN_F2 }, - { SONYPI_EVENT_FNKEY_F3, KEY_FN_F3 }, - { SONYPI_EVENT_FNKEY_F4, KEY_FN_F4 }, - { SONYPI_EVENT_FNKEY_F5, KEY_FN_F5 }, - { SONYPI_EVENT_FNKEY_F6, KEY_FN_F6 }, - { SONYPI_EVENT_FNKEY_F7, KEY_FN_F7 }, - { SONYPI_EVENT_FNKEY_F8, KEY_FN_F8 }, - { SONYPI_EVENT_FNKEY_F9, KEY_FN_F9 }, - { SONYPI_EVENT_FNKEY_F10, KEY_FN_F10 }, - { SONYPI_EVENT_FNKEY_F11, KEY_FN_F11 }, - { SONYPI_EVENT_FNKEY_F12, KEY_FN_F12 }, - { SONYPI_EVENT_FNKEY_1, KEY_FN_1 }, - { SONYPI_EVENT_FNKEY_2, KEY_FN_2 }, - { SONYPI_EVENT_FNKEY_D, KEY_FN_D }, - { SONYPI_EVENT_FNKEY_E, KEY_FN_E }, - { SONYPI_EVENT_FNKEY_F, KEY_FN_F }, - { SONYPI_EVENT_FNKEY_S, KEY_FN_S }, - { SONYPI_EVENT_FNKEY_B, KEY_FN_B }, - { SONYPI_EVENT_BLUETOOTH_PRESSED, KEY_BLUE }, - { SONYPI_EVENT_BLUETOOTH_ON, KEY_BLUE }, - { SONYPI_EVENT_PKEY_P1, KEY_PROG1 }, - { SONYPI_EVENT_PKEY_P2, KEY_PROG2 }, - { SONYPI_EVENT_PKEY_P3, KEY_PROG3 }, - { SONYPI_EVENT_BACK_PRESSED, KEY_BACK }, - { SONYPI_EVENT_HELP_PRESSED, KEY_HELP }, - { SONYPI_EVENT_ZOOM_PRESSED, KEY_ZOOM }, - { SONYPI_EVENT_THUMBPHRASE_PRESSED, BTN_THUMB }, - { 0, 0 }, +/* Correspondance table between sonypi events + * and input layer indexes in the keymap + */ +static int sony_laptop_input_index[] = { + -1, /* no event */ + -1, /* SONYPI_EVENT_JOGDIAL_DOWN */ + -1, /* SONYPI_EVENT_JOGDIAL_UP */ + -1, /* SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */ + -1, /* SONYPI_EVENT_JOGDIAL_UP_PRESSED */ + -1, /* SONYPI_EVENT_JOGDIAL_PRESSED */ + -1, /* SONYPI_EVENT_JOGDIAL_RELEASED */ + 0, /* SONYPI_EVENT_CAPTURE_PRESSED */ + 1, /* SONYPI_EVENT_CAPTURE_RELEASED */ + 2, /* SONYPI_EVENT_CAPTURE_PARTIALPRESSED */ + 3, /* SONYPI_EVENT_CAPTURE_PARTIALRELEASED */ + 4, /* SONYPI_EVENT_FNKEY_ESC */ + 5, /* SONYPI_EVENT_FNKEY_F1 */ + 6, /* SONYPI_EVENT_FNKEY_F2 */ + 7, /* SONYPI_EVENT_FNKEY_F3 */ + 8, /* SONYPI_EVENT_FNKEY_F4 */ + 9, /* SONYPI_EVENT_FNKEY_F5 */ + 10, /* SONYPI_EVENT_FNKEY_F6 */ + 11, /* SONYPI_EVENT_FNKEY_F7 */ + 12, /* SONYPI_EVENT_FNKEY_F8 */ + 13, /* SONYPI_EVENT_FNKEY_F9 */ + 14, /* SONYPI_EVENT_FNKEY_F10 */ + 15, /* SONYPI_EVENT_FNKEY_F11 */ + 16, /* SONYPI_EVENT_FNKEY_F12 */ + 17, /* SONYPI_EVENT_FNKEY_1 */ + 18, /* SONYPI_EVENT_FNKEY_2 */ + 19, /* SONYPI_EVENT_FNKEY_D */ + 20, /* SONYPI_EVENT_FNKEY_E */ + 21, /* SONYPI_EVENT_FNKEY_F */ + 22, /* SONYPI_EVENT_FNKEY_S */ + 23, /* SONYPI_EVENT_FNKEY_B */ + 24, /* SONYPI_EVENT_BLUETOOTH_PRESSED */ + 25, /* SONYPI_EVENT_PKEY_P1 */ + 26, /* SONYPI_EVENT_PKEY_P2 */ + 27, /* SONYPI_EVENT_PKEY_P3 */ + 28, /* SONYPI_EVENT_BACK_PRESSED */ + -1, /* SONYPI_EVENT_LID_CLOSED */ + -1, /* SONYPI_EVENT_LID_OPENED */ + 29, /* SONYPI_EVENT_BLUETOOTH_ON */ + 30, /* SONYPI_EVENT_BLUETOOTH_OFF */ + 31, /* SONYPI_EVENT_HELP_PRESSED */ + 32, /* SONYPI_EVENT_FNKEY_ONLY */ + 33, /* SONYPI_EVENT_JOGDIAL_FAST_DOWN */ + 34, /* SONYPI_EVENT_JOGDIAL_FAST_UP */ + 35, /* SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */ + 36, /* SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */ + 37, /* SONYPI_EVENT_JOGDIAL_VFAST_DOWN */ + 38, /* SONYPI_EVENT_JOGDIAL_VFAST_UP */ + 39, /* SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */ + 40, /* SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */ + 41, /* SONYPI_EVENT_ZOOM_PRESSED */ + 42, /* SONYPI_EVENT_THUMBPHRASE_PRESSED */ + 43, /* SONYPI_EVENT_MEYE_FACE */ + 44, /* SONYPI_EVENT_MEYE_OPPOSITE */ + 45, /* SONYPI_EVENT_MEMORYSTICK_INSERT */ + 46, /* SONYPI_EVENT_MEMORYSTICK_EJECT */ + -1, /* SONYPI_EVENT_ANYBUTTON_RELEASED */ + -1, /* SONYPI_EVENT_BATTERY_INSERT */ + -1, /* SONYPI_EVENT_BATTERY_REMOVE */ + -1, /* SONYPI_EVENT_FNKEY_RELEASED */ + 47, /* SONYPI_EVENT_WIRELESS_ON */ + 48, /* SONYPI_EVENT_WIRELESS_OFF */ +}; + +static int sony_laptop_input_keycode_map[] = { + KEY_CAMERA, /* 0 SONYPI_EVENT_CAPTURE_PRESSED */ + KEY_RESERVED, /* 1 SONYPI_EVENT_CAPTURE_RELEASED */ + KEY_RESERVED, /* 2 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */ + KEY_RESERVED, /* 3 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */ + KEY_FN_ESC, /* 4 SONYPI_EVENT_FNKEY_ESC */ + KEY_FN_F1, /* 5 SONYPI_EVENT_FNKEY_F1 */ + KEY_FN_F2, /* 6 SONYPI_EVENT_FNKEY_F2 */ + KEY_FN_F3, /* 7 SONYPI_EVENT_FNKEY_F3 */ + KEY_FN_F4, /* 8 SONYPI_EVENT_FNKEY_F4 */ + KEY_FN_F5, /* 9 SONYPI_EVENT_FNKEY_F5 */ + KEY_FN_F6, /* 10 SONYPI_EVENT_FNKEY_F6 */ + KEY_FN_F7, /* 11 SONYPI_EVENT_FNKEY_F7 */ + KEY_FN_F8, /* 12 SONYPI_EVENT_FNKEY_F8 */ + KEY_FN_F9, /* 13 SONYPI_EVENT_FNKEY_F9 */ + KEY_FN_F10, /* 14 SONYPI_EVENT_FNKEY_F10 */ + KEY_FN_F11, /* 15 SONYPI_EVENT_FNKEY_F11 */ + KEY_FN_F12, /* 16 SONYPI_EVENT_FNKEY_F12 */ + KEY_FN_F1, /* 17 SONYPI_EVENT_FNKEY_1 */ + KEY_FN_F2, /* 18 SONYPI_EVENT_FNKEY_2 */ + KEY_FN_D, /* 19 SONYPI_EVENT_FNKEY_D */ + KEY_FN_E, /* 20 SONYPI_EVENT_FNKEY_E */ + KEY_FN_F, /* 21 SONYPI_EVENT_FNKEY_F */ + KEY_FN_S, /* 22 SONYPI_EVENT_FNKEY_S */ + KEY_FN_B, /* 23 SONYPI_EVENT_FNKEY_B */ + KEY_BLUETOOTH, /* 24 SONYPI_EVENT_BLUETOOTH_PRESSED */ + KEY_PROG1, /* 25 SONYPI_EVENT_PKEY_P1 */ + KEY_PROG2, /* 26 SONYPI_EVENT_PKEY_P2 */ + KEY_PROG3, /* 27 SONYPI_EVENT_PKEY_P3 */ + KEY_BACK, /* 28 SONYPI_EVENT_BACK_PRESSED */ + KEY_BLUETOOTH, /* 29 SONYPI_EVENT_BLUETOOTH_ON */ + KEY_BLUETOOTH, /* 30 SONYPI_EVENT_BLUETOOTH_OFF */ + KEY_HELP, /* 31 SONYPI_EVENT_HELP_PRESSED */ + KEY_FN, /* 32 SONYPI_EVENT_FNKEY_ONLY */ + KEY_RESERVED, /* 33 SONYPI_EVENT_JOGDIAL_FAST_DOWN */ + KEY_RESERVED, /* 34 SONYPI_EVENT_JOGDIAL_FAST_UP */ + KEY_RESERVED, /* 35 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */ + KEY_RESERVED, /* 36 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */ + KEY_RESERVED, /* 37 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */ + KEY_RESERVED, /* 38 SONYPI_EVENT_JOGDIAL_VFAST_UP */ + KEY_RESERVED, /* 39 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */ + KEY_RESERVED, /* 40 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */ + KEY_ZOOM, /* 41 SONYPI_EVENT_ZOOM_PRESSED */ + BTN_THUMB, /* 42 SONYPI_EVENT_THUMBPHRASE_PRESSED */ + KEY_RESERVED, /* 43 SONYPI_EVENT_MEYE_FACE */ + KEY_RESERVED, /* 44 SONYPI_EVENT_MEYE_OPPOSITE */ + KEY_RESERVED, /* 45 SONYPI_EVENT_MEMORYSTICK_INSERT */ + KEY_RESERVED, /* 46 SONYPI_EVENT_MEMORYSTICK_EJECT */ + KEY_WLAN, /* 47 SONYPI_EVENT_WIRELESS_ON */ + KEY_WLAN, /* 48 SONYPI_EVENT_WIRELESS_OFF */ }; /* release buttons after a short delay if pressed */ @@ -202,7 +283,6 @@ static void sony_laptop_report_input_event(u8 event) struct input_dev *jog_dev = sony_laptop_input.jog_dev; struct input_dev *key_dev = sony_laptop_input.key_dev; struct sony_laptop_keypress kp = { NULL }; - int i; if (event == SONYPI_EVENT_FNKEY_RELEASED) { /* Nothing, not all VAIOs generate this event */ @@ -231,17 +311,22 @@ static void sony_laptop_report_input_event(u8 event) break; default: - for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++) - if (event == sony_laptop_inputkeys[i].sonypiev) { + if (event > ARRAY_SIZE (sony_laptop_input_keycode_map)) { + dprintk("sony_laptop_report_input_event, event not known: %d\n", event); + break; + } + if (sony_laptop_input_index[event] != -1) { + kp.key = sony_laptop_input_keycode_map[sony_laptop_input_index[event]]; + if (kp.key != KEY_UNKNOWN) kp.dev = key_dev; - kp.key = sony_laptop_inputkeys[i].inputev; - break; - } + } break; } if (kp.dev) { input_report_key(kp.dev, kp.key, 1); + /* we emit the scancode so we can always remap the key */ + input_event(kp.dev, EV_MSC, MSC_SCAN, event); input_sync(kp.dev); kfifo_put(sony_laptop_input.fifo, (unsigned char *)&kp, sizeof(kp)); @@ -296,11 +381,18 @@ static int sony_laptop_setup_input(void) key_dev->id.vendor = PCI_VENDOR_ID_SONY; /* Initialize the Input Drivers: special keys */ - key_dev->evbit[0] = BIT(EV_KEY); - for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++) - if (sony_laptop_inputkeys[i].inputev) - set_bit(sony_laptop_inputkeys[i].inputev, - key_dev->keybit); + set_bit(EV_KEY, key_dev->evbit); + set_bit(EV_MSC, key_dev->evbit); + set_bit(MSC_SCAN, key_dev->mscbit); + key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]); + key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map); + key_dev->keycode = &sony_laptop_input_keycode_map; + for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) { + if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) { + set_bit(sony_laptop_input_keycode_map[i], + key_dev->keybit); + } + } error = input_register_device(key_dev); if (error) @@ -487,6 +579,14 @@ SNC_HANDLE_NAMES(audiopower_set, "AZPW"); SNC_HANDLE_NAMES(lanpower_get, "GLNP"); SNC_HANDLE_NAMES(lanpower_set, "LNPW"); +SNC_HANDLE_NAMES(lidstate_get, "GLID"); + +SNC_HANDLE_NAMES(indicatorlamp_get, "GILS"); +SNC_HANDLE_NAMES(indicatorlamp_set, "SILS"); + +SNC_HANDLE_NAMES(gainbass_get, "GMGB"); +SNC_HANDLE_NAMES(gainbass_set, "CMGB"); + SNC_HANDLE_NAMES(PID_get, "GPID"); SNC_HANDLE_NAMES(CTR_get, "GCTR"); @@ -507,6 +607,12 @@ static struct sony_nc_value sony_nc_values[] = { boolean_validate, 0), SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set, boolean_validate, 1), + SNC_HANDLE(lidstate, snc_lidstate_get, NULL, + boolean_validate, 0), + SNC_HANDLE(indicatorlamp, snc_indicatorlamp_get, snc_indicatorlamp_set, + boolean_validate, 0), + SNC_HANDLE(gainbass, snc_gainbass_get, snc_gainbass_set, + boolean_validate, 0), /* unknown methods */ SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1), SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1), @@ -689,13 +795,116 @@ static struct backlight_ops sony_backlight_ops = { }; /* + * New SNC-only Vaios event mapping to driver known keys + */ +struct sony_nc_event { + u8 data; + u8 event; +}; + +static struct sony_nc_event *sony_nc_events; + +/* Vaio C* --maybe also FE*, N* and AR* ?-- special init sequence + * for Fn keys + */ +static int sony_nc_C_enable(struct dmi_system_id *id) +{ + int result = 0; + + printk(KERN_NOTICE DRV_PFX "detected %s\n", id->ident); + + sony_nc_events = id->driver_data; + + if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x4, &result) < 0 + || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x2, &result) < 0 + || acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x10, &result) < 0 + || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x0, &result) < 0 + || acpi_callsetfunc(sony_nc_acpi_handle, "SN03", 0x2, &result) < 0 + || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x101, &result) < 0) { + printk(KERN_WARNING DRV_PFX "failed to initialize SNC, some " + "functionalities may be missing\n"); + return 1; + } + return 0; +} + +static struct sony_nc_event sony_C_events[] = { + { 0x81, SONYPI_EVENT_FNKEY_F1 }, + { 0x01, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x85, SONYPI_EVENT_FNKEY_F5 }, + { 0x05, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x86, SONYPI_EVENT_FNKEY_F6 }, + { 0x06, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x87, SONYPI_EVENT_FNKEY_F7 }, + { 0x07, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x8A, SONYPI_EVENT_FNKEY_F10 }, + { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x8C, SONYPI_EVENT_FNKEY_F12 }, + { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, + { 0, 0 }, +}; + +/* SNC-only model map */ +struct dmi_system_id sony_nc_ids[] = { + { + .ident = "Sony Vaio FE Series", + .callback = sony_nc_C_enable, + .driver_data = sony_C_events, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FE"), + }, + }, + { + .ident = "Sony Vaio C Series", + .callback = sony_nc_C_enable, + .driver_data = sony_C_events, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-C"), + }, + }, + { } +}; + +/* * ACPI callbacks */ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) { - dprintk("sony_acpi_notify, event: %d\n", event); - sony_laptop_report_input_event(event); - acpi_bus_generate_event(sony_nc_acpi_device, 1, event); + struct sony_nc_event *evmap; + u32 ev = event; + int result; + + if (ev == 0x92) { + /* read the key pressed from EC.GECR + * A call to SN07 with 0x0202 will do it as well respecting + * the current protocol on different OSes + * + * Note: the path for GECR may be + * \_SB.PCI0.LPCB.EC (C, FE, AR, N and friends) + * \_SB.PCI0.PIB.EC0 (VGN-FR notifications are sent directly, no GECR) + * + * TODO: we may want to do the same for the older GHKE -need + * dmi list- so this snippet may become one more callback. + */ + if (acpi_callsetfunc(handle, "SN07", 0x0202, &result) < 0) + dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev); + else + ev = result & 0xFF; + } + + if (sony_nc_events) + for (evmap = sony_nc_events; evmap->event; evmap++) { + if (evmap->data == ev) { + ev = evmap->event; + break; + } + } + + dprintk("sony_acpi_notify, event: 0x%.2x\n", ev); + sony_laptop_report_input_event(ev); + acpi_bus_generate_event(sony_nc_acpi_device, 1, ev); } static acpi_status sony_walk_callback(acpi_handle handle, u32 level, @@ -732,6 +941,10 @@ static int sony_nc_resume(struct acpi_device *device) break; } } + + /* re-initialize models with specific requirements */ + dmi_check_system(sony_nc_ids); + return 0; } @@ -750,6 +963,15 @@ static int sony_nc_add(struct acpi_device *device) sony_nc_acpi_handle = device->handle; + /* read device status */ + result = acpi_bus_get_status(device); + /* bail IFF the above call was successful and the device is not present */ + if (!result && !device->status.present) { + dprintk("Device not present\n"); + result = -ENODEV; + goto outwalk; + } + if (debug) { status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle, 1, sony_walk_callback, NULL, NULL); @@ -760,6 +982,15 @@ static int sony_nc_add(struct acpi_device *device) } } + /* try to _INI the device if such method exists (ACPI spec 3.0-6.5.1 + * should be respected as we already checked for the device presence above */ + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, METHOD_NAME__INI, &handle))) { + dprintk("Invoking _INI\n"); + if (ACPI_FAILURE(acpi_evaluate_object(sony_nc_acpi_handle, METHOD_NAME__INI, + NULL, NULL))) + dprintk("_INI Method failed\n"); + } + /* setup input devices and helper fifo */ result = sony_laptop_setup_input(); if (result) { @@ -772,7 +1003,7 @@ static int sony_nc_add(struct acpi_device *device) ACPI_DEVICE_NOTIFY, sony_acpi_notify, NULL); if (ACPI_FAILURE(status)) { - printk(KERN_WARNING DRV_PFX "unable to install notify handler\n"); + printk(KERN_WARNING DRV_PFX "unable to install notify handler (%u)\n", status); result = -ENODEV; goto outinput; } @@ -795,6 +1026,9 @@ static int sony_nc_add(struct acpi_device *device) } + /* initialize models with specific requirements */ + dmi_check_system(sony_nc_ids); + result = sony_pf_add(); if (result) goto outbacklight; @@ -908,7 +1142,9 @@ static struct acpi_driver sony_nc_driver = { #define SONYPI_DEVICE_TYPE2 0x00000002 #define SONYPI_DEVICE_TYPE3 0x00000004 -#define SONY_PIC_EV_MASK 0xff +#define SONYPI_TYPE1_OFFSET 0x04 +#define SONYPI_TYPE2_OFFSET 0x12 +#define SONYPI_TYPE3_OFFSET 0x12 struct sony_pic_ioport { struct acpi_resource_io io; @@ -922,6 +1158,7 @@ struct sony_pic_irq { struct sony_pic_dev { int model; + u16 evport_offset; u8 camera_power; u8 bluetooth_power; u8 wwan_power; @@ -1999,20 +2236,17 @@ end: static irqreturn_t sony_pic_irq(int irq, void *dev_id) { int i, j; - u32 port_val = 0; u8 ev = 0; u8 data_mask = 0; u8 device_event = 0; struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id; - acpi_os_read_port(dev->cur_ioport->io.minimum, &port_val, - dev->cur_ioport->io.address_length); - ev = port_val & SONY_PIC_EV_MASK; - data_mask = 0xff & (port_val >> (dev->cur_ioport->io.address_length - 8)); + ev = inb_p(dev->cur_ioport->io.minimum); + data_mask = inb_p(dev->cur_ioport->io.minimum + dev->evport_offset); - dprintk("event (0x%.8x [%.2x] [%.2x]) at port 0x%.4x\n", - port_val, ev, data_mask, dev->cur_ioport->io.minimum); + dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n", + ev, data_mask, dev->cur_ioport->io.minimum, dev->evport_offset); if (ev == 0x00 || ev == 0xff) return IRQ_HANDLED; @@ -2103,6 +2337,20 @@ static int sony_pic_add(struct acpi_device *device) spic_dev.model = sony_pic_detect_device_type(); mutex_init(&spic_dev.lock); + /* model specific characteristics */ + switch(spic_dev.model) { + case SONYPI_DEVICE_TYPE1: + spic_dev.evport_offset = SONYPI_TYPE1_OFFSET; + break; + case SONYPI_DEVICE_TYPE3: + spic_dev.evport_offset = SONYPI_TYPE3_OFFSET; + break; + case SONYPI_DEVICE_TYPE2: + default: + spic_dev.evport_offset = SONYPI_TYPE2_OFFSET; + break; + } + /* read _PRS resources */ result = sony_pic_possible_resources(device); if (result) { diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 95c0b96e83f..f15a58f7403 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -21,8 +21,8 @@ * 02110-1301, USA. */ -#define IBM_VERSION "0.14" -#define TPACPI_SYSFS_VERSION 0x000100 +#define IBM_VERSION "0.15" +#define TPACPI_SYSFS_VERSION 0x010000 /* * Changelog: @@ -92,6 +92,29 @@ MODULE_LICENSE("GPL"); /* Please remove this in year 2009 */ MODULE_ALIAS("ibm_acpi"); +/* + * DMI matching for module autoloading + * + * See http://thinkwiki.org/wiki/List_of_DMI_IDs + * See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads + * + * Only models listed in thinkwiki will be supported, so add yours + * if it is not there yet. + */ +#define IBM_BIOS_MODULE_ALIAS(__type) \ + MODULE_ALIAS("dmi:bvnIBM:bvr" __type "ET??WW") + +/* Non-ancient thinkpads */ +MODULE_ALIAS("dmi:bvnIBM:*:svnIBM:*:pvrThinkPad*:rvnIBM:*"); +MODULE_ALIAS("dmi:bvnLENOVO:*:svnLENOVO:*:pvrThinkPad*:rvnLENOVO:*"); + +/* Ancient thinkpad BIOSes have to be identified by + * BIOS type or model number, and there are far less + * BIOS types than model numbers... */ +IBM_BIOS_MODULE_ALIAS("I[B,D,H,I,M,N,O,T,W,V,Y,Z]"); +IBM_BIOS_MODULE_ALIAS("1[0,3,6,8,A-G,I,K,M-P,S,T]"); +IBM_BIOS_MODULE_ALIAS("K[U,X-Z]"); + #define __unused __attribute__ ((unused)) /**************************************************************************** @@ -106,7 +129,7 @@ MODULE_ALIAS("ibm_acpi"); * ACPI basic handles */ -static acpi_handle root_handle = NULL; +static acpi_handle root_handle; #define IBM_HANDLE(object, parent, paths...) \ static acpi_handle object##_handle; \ @@ -487,19 +510,36 @@ static char *next_cmd(char **cmds) /**************************************************************************** **************************************************************************** * - * Device model: hwmon and platform + * Device model: input, hwmon and platform * **************************************************************************** ****************************************************************************/ -static struct platform_device *tpacpi_pdev = NULL; -static struct class_device *tpacpi_hwmon = NULL; +static struct platform_device *tpacpi_pdev; +static struct class_device *tpacpi_hwmon; +static struct input_dev *tpacpi_inputdev; + + +static int tpacpi_resume_handler(struct platform_device *pdev) +{ + struct ibm_struct *ibm, *itmp; + + list_for_each_entry_safe(ibm, itmp, + &tpacpi_all_drivers, + all_drivers) { + if (ibm->resume) + (ibm->resume)(); + } + + return 0; +} static struct platform_driver tpacpi_pdriver = { .driver = { .name = IBM_DRVR_NAME, .owner = THIS_MODULE, }, + .resume = tpacpi_resume_handler, }; @@ -677,9 +717,19 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); printk(IBM_INFO "%s\n", IBM_URL); - if (ibm_thinkpad_ec_found) - printk(IBM_INFO "ThinkPad EC firmware %s\n", - ibm_thinkpad_ec_found); + printk(IBM_INFO "ThinkPad BIOS %s, EC %s\n", + (thinkpad_id.bios_version_str) ? + thinkpad_id.bios_version_str : "unknown", + (thinkpad_id.ec_version_str) ? + thinkpad_id.ec_version_str : "unknown"); + + if (thinkpad_id.vendor && thinkpad_id.model_str) + printk(IBM_INFO "%s %s\n", + (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? + "IBM" : ((thinkpad_id.vendor == + PCI_VENDOR_ID_LENOVO) ? + "Lenovo" : "Unknown vendor"), + thinkpad_id.model_str); return 0; } @@ -704,16 +754,28 @@ static struct ibm_struct thinkpad_acpi_driver_data = { */ static int hotkey_orig_status; -static int hotkey_orig_mask; +static u32 hotkey_orig_mask; +static u32 hotkey_all_mask; +static u32 hotkey_reserved_mask; + +static u16 *hotkey_keycode_map; -static struct attribute_set *hotkey_dev_attributes = NULL; +static struct attribute_set *hotkey_dev_attributes; + +static int hotkey_get_wlsw(int *status) +{ + if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) + return -EIO; + return 0; +} /* sysfs hotkey enable ------------------------------------------------- */ static ssize_t hotkey_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { - int res, status, mask; + int res, status; + u32 mask; res = hotkey_get(&status, &mask); if (res) @@ -727,7 +789,8 @@ static ssize_t hotkey_enable_store(struct device *dev, const char *buf, size_t count) { unsigned long t; - int res, status, mask; + int res, status; + u32 mask; if (parse_strtoul(buf, 1, &t)) return -EINVAL; @@ -748,13 +811,14 @@ static ssize_t hotkey_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { - int res, status, mask; + int res, status; + u32 mask; res = hotkey_get(&status, &mask); if (res) return res; - return snprintf(buf, PAGE_SIZE, "0x%04x\n", mask); + return snprintf(buf, PAGE_SIZE, "0x%08x\n", mask); } static ssize_t hotkey_mask_store(struct device *dev, @@ -762,9 +826,10 @@ static ssize_t hotkey_mask_store(struct device *dev, const char *buf, size_t count) { unsigned long t; - int res, status, mask; + int res, status; + u32 mask; - if (parse_strtoul(buf, 0xffff, &t)) + if (parse_strtoul(buf, 0xffffffffUL, &t)) return -EINVAL; res = hotkey_get(&status, &mask); @@ -794,26 +859,123 @@ static ssize_t hotkey_bios_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "0x%04x\n", hotkey_orig_mask); + return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask); } static struct device_attribute dev_attr_hotkey_bios_mask = __ATTR(hotkey_bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL); +/* sysfs hotkey all_mask ----------------------------------------------- */ +static ssize_t hotkey_all_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_all_mask); +} + +static struct device_attribute dev_attr_hotkey_all_mask = + __ATTR(hotkey_all_mask, S_IRUGO, hotkey_all_mask_show, NULL); + +/* sysfs hotkey recommended_mask --------------------------------------- */ +static ssize_t hotkey_recommended_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%08x\n", + hotkey_all_mask & ~hotkey_reserved_mask); +} + +static struct device_attribute dev_attr_hotkey_recommended_mask = + __ATTR(hotkey_recommended_mask, S_IRUGO, + hotkey_recommended_mask_show, NULL); + +/* sysfs hotkey radio_sw ----------------------------------------------- */ +static ssize_t hotkey_radio_sw_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res, s; + res = hotkey_get_wlsw(&s); + if (res < 0) + return res; + + return snprintf(buf, PAGE_SIZE, "%d\n", !!s); +} + +static struct device_attribute dev_attr_hotkey_radio_sw = + __ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_show, NULL); + /* --------------------------------------------------------------------- */ static struct attribute *hotkey_mask_attributes[] = { &dev_attr_hotkey_mask.attr, &dev_attr_hotkey_bios_enabled.attr, &dev_attr_hotkey_bios_mask.attr, + &dev_attr_hotkey_all_mask.attr, + &dev_attr_hotkey_recommended_mask.attr, }; static int __init hotkey_init(struct ibm_init_struct *iibm) { - int res; + + static u16 ibm_keycode_map[] __initdata = { + /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */ + KEY_FN_F1, KEY_FN_F2, KEY_COFFEE, KEY_SLEEP, + KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8, + KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND, + /* Scan codes 0x0C to 0x0F: Other ACPI HKEY hot keys */ + KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */ + KEY_UNKNOWN, /* 0x0D: FN+INSERT */ + KEY_UNKNOWN, /* 0x0E: FN+DELETE */ + KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ + /* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */ + KEY_RESERVED, /* 0x10: FN+END (brightness down) */ + KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ + KEY_UNKNOWN, /* 0x12: FN+PGDOWN */ + KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */ + KEY_RESERVED, /* 0x14: VOLUME UP */ + KEY_RESERVED, /* 0x15: VOLUME DOWN */ + KEY_RESERVED, /* 0x16: MUTE */ + KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */ + /* (assignments unknown, please report if found) */ + KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + }; + static u16 lenovo_keycode_map[] __initdata = { + /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */ + KEY_FN_F1, KEY_COFFEE, KEY_BATTERY, KEY_SLEEP, + KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8, + KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND, + /* Scan codes 0x0C to 0x0F: Other ACPI HKEY hot keys */ + KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */ + KEY_UNKNOWN, /* 0x0D: FN+INSERT */ + KEY_UNKNOWN, /* 0x0E: FN+DELETE */ + KEY_BRIGHTNESSUP, /* 0x0F: FN+HOME (brightness up) */ + /* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */ + KEY_BRIGHTNESSDOWN, /* 0x10: FN+END (brightness down) */ + KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ + KEY_UNKNOWN, /* 0x12: FN+PGDOWN */ + KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */ + KEY_RESERVED, /* 0x14: VOLUME UP */ + KEY_RESERVED, /* 0x15: VOLUME DOWN */ + KEY_RESERVED, /* 0x16: MUTE */ + KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */ + /* (assignments unknown, please report if found) */ + KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + }; + +#define TPACPI_HOTKEY_MAP_LEN ARRAY_SIZE(ibm_keycode_map) +#define TPACPI_HOTKEY_MAP_SIZE sizeof(ibm_keycode_map) +#define TPACPI_HOTKEY_MAP_TYPESIZE sizeof(ibm_keycode_map[0]) + + int res, i; + int status; vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); + BUG_ON(!tpacpi_inputdev); + IBM_ACPIHANDLE_INIT(hkey); mutex_init(&hotkey_mutex); @@ -824,7 +986,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) str_supported(tp_features.hotkey)); if (tp_features.hotkey) { - hotkey_dev_attributes = create_attr_set(4, NULL); + hotkey_dev_attributes = create_attr_set(7, NULL); if (!hotkey_dev_attributes) return -ENOMEM; res = add_to_attr_set(hotkey_dev_attributes, @@ -840,19 +1002,92 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", str_supported(tp_features.hotkey_mask)); + if (tp_features.hotkey_mask) { + /* MHKA available in A31, R40, R40e, T4x, X31, and later */ + if (!acpi_evalf(hkey_handle, &hotkey_all_mask, + "MHKA", "qd")) + hotkey_all_mask = 0x080cU; /* FN+F12, FN+F4, FN+F3 */ + } + res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask); if (!res && tp_features.hotkey_mask) { res = add_many_to_attr_set(hotkey_dev_attributes, hotkey_mask_attributes, ARRAY_SIZE(hotkey_mask_attributes)); } + + /* Not all thinkpads have a hardware radio switch */ + if (!res && acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { + tp_features.hotkey_wlsw = 1; + printk(IBM_INFO + "radio switch found; radios are %s\n", + enabled(status, 0)); + res = add_to_attr_set(hotkey_dev_attributes, + &dev_attr_hotkey_radio_sw.attr); + } + if (!res) res = register_attr_set_with_sysfs( hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); + if (res) + return res; + + /* Set up key map */ + + hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE, + GFP_KERNEL); + if (!hotkey_keycode_map) { + printk(IBM_ERR "failed to allocate memory for key map\n"); + return -ENOMEM; + } + + if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { + dbg_printk(TPACPI_DBG_INIT, + "using Lenovo default hot key map\n"); + memcpy(hotkey_keycode_map, &lenovo_keycode_map, + TPACPI_HOTKEY_MAP_SIZE); + } else { + dbg_printk(TPACPI_DBG_INIT, + "using IBM default hot key map\n"); + memcpy(hotkey_keycode_map, &ibm_keycode_map, + TPACPI_HOTKEY_MAP_SIZE); + } +#ifndef CONFIG_THINKPAD_ACPI_INPUT_ENABLED + for (i = 0; i < 12; i++) + hotkey_keycode_map[i] = KEY_UNKNOWN; +#endif /* ! CONFIG_THINKPAD_ACPI_INPUT_ENABLED */ + + set_bit(EV_KEY, tpacpi_inputdev->evbit); + set_bit(EV_MSC, tpacpi_inputdev->evbit); + set_bit(MSC_SCAN, tpacpi_inputdev->mscbit); + tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; + tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; + tpacpi_inputdev->keycode = hotkey_keycode_map; + for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { + if (hotkey_keycode_map[i] != KEY_RESERVED) { + set_bit(hotkey_keycode_map[i], + tpacpi_inputdev->keybit); + } else { + if (i < sizeof(hotkey_reserved_mask)*8) + hotkey_reserved_mask |= 1 << i; + } + } + + if (tp_features.hotkey_wlsw) { + set_bit(EV_SW, tpacpi_inputdev->evbit); + set_bit(SW_RADIO, tpacpi_inputdev->swbit); + } + +#ifdef CONFIG_THINKPAD_ACPI_INPUT_ENABLED + dbg_printk(TPACPI_DBG_INIT, + "enabling hot key handling\n"); + res = hotkey_set(1, (hotkey_all_mask & ~hotkey_reserved_mask) + | hotkey_orig_mask); if (res) return res; +#endif /* CONFIG_THINKPAD_ACPI_INPUT_ENABLED */ } return (tp_features.hotkey)? 0 : 1; @@ -875,22 +1110,101 @@ static void hotkey_exit(void) } } +static void tpacpi_input_send_key(unsigned int scancode, + unsigned int keycode) +{ + if (keycode != KEY_RESERVED) { + input_report_key(tpacpi_inputdev, keycode, 1); + if (keycode == KEY_UNKNOWN) + input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, + scancode); + input_sync(tpacpi_inputdev); + + input_report_key(tpacpi_inputdev, keycode, 0); + if (keycode == KEY_UNKNOWN) + input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, + scancode); + input_sync(tpacpi_inputdev); + } +} + +static void tpacpi_input_send_radiosw(void) +{ + int wlsw; + + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) + input_report_switch(tpacpi_inputdev, + SW_RADIO, !!wlsw); +} + static void hotkey_notify(struct ibm_struct *ibm, u32 event) { - int hkey; + u32 hkey; + unsigned int keycode, scancode; + int sendacpi = 1; + + if (event == 0x80 && acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { + if (tpacpi_inputdev->users > 0) { + switch (hkey >> 12) { + case 1: + /* 0x1000-0x1FFF: key presses */ + scancode = hkey & 0xfff; + if (scancode > 0 && scancode < 0x21) { + scancode--; + keycode = hotkey_keycode_map[scancode]; + tpacpi_input_send_key(scancode, keycode); + sendacpi = (keycode == KEY_RESERVED + || keycode == KEY_UNKNOWN); + } else { + printk(IBM_ERR + "hotkey 0x%04x out of range for keyboard map\n", + hkey); + } + break; + case 5: + /* 0x5000-0x5FFF: LID */ + /* we don't handle it through this path, just + * eat up known LID events */ + if (hkey != 0x5001 && hkey != 0x5002) { + printk(IBM_ERR + "unknown LID-related hotkey event: 0x%04x\n", + hkey); + } + break; + case 7: + /* 0x7000-0x7FFF: misc */ + if (tp_features.hotkey_wlsw && hkey == 0x7000) { + tpacpi_input_send_radiosw(); + sendacpi = 0; + break; + } + /* fallthrough to default */ + default: + /* case 2: dock-related */ + /* 0x2305 - T43 waking up due to bay lever eject while aslept */ + /* case 3: ultra-bay related. maybe bay in dock? */ + /* 0x3003 - T43 after wake up by bay lever eject (0x2305) */ + printk(IBM_NOTICE "unhandled hotkey event 0x%04x\n", hkey); + } + } - if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) - acpi_bus_generate_event(ibm->acpi->device, event, hkey); - else { - printk(IBM_ERR "unknown hotkey event %d\n", event); + if (sendacpi) + acpi_bus_generate_event(ibm->acpi->device, event, hkey); + } else { + printk(IBM_ERR "unknown hotkey notification event %d\n", event); acpi_bus_generate_event(ibm->acpi->device, event, 0); } } +static void hotkey_resume(void) +{ + tpacpi_input_send_radiosw(); +} + /* * Call with hotkey_mutex held */ -static int hotkey_get(int *status, int *mask) +static int hotkey_get(int *status, u32 *mask) { if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) return -EIO; @@ -905,7 +1219,7 @@ static int hotkey_get(int *status, int *mask) /* * Call with hotkey_mutex held */ -static int hotkey_set(int status, int mask) +static int hotkey_set(int status, u32 mask) { int i; @@ -926,7 +1240,8 @@ static int hotkey_set(int status, int mask) /* procfs -------------------------------------------------------------- */ static int hotkey_read(char *p) { - int res, status, mask; + int res, status; + u32 mask; int len = 0; if (!tp_features.hotkey) { @@ -944,7 +1259,7 @@ static int hotkey_read(char *p) len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); if (tp_features.hotkey_mask) { - len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); + len += sprintf(p + len, "mask:\t\t0x%08x\n", mask); len += sprintf(p + len, "commands:\tenable, disable, reset, <mask>\n"); } else { @@ -957,7 +1272,8 @@ static int hotkey_read(char *p) static int hotkey_write(char *buf) { - int res, status, mask; + int res, status; + u32 mask; char *cmd; int do_cmd = 0; @@ -1012,6 +1328,7 @@ static struct ibm_struct hotkey_driver_data = { .read = hotkey_read, .write = hotkey_write, .exit = hotkey_exit, + .resume = hotkey_resume, .acpi = &ibm_hotkey_acpidriver, }; @@ -1770,7 +2087,10 @@ static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = { .type = ACPI_SYSTEM_NOTIFY, }, { - .hid = IBM_PCI_HID, + /* THIS ONE MUST NEVER BE USED FOR DRIVER AUTOLOADING. + * We just use it to get notifications of dock hotplug + * in very old thinkpads */ + .hid = PCI_ROOT_HID_STRING, .notify = dock_notify, .handle = &pci_handle, .type = ACPI_SYSTEM_NOTIFY, @@ -1829,7 +2149,7 @@ static int __init dock_init2(struct ibm_init_struct *iibm) static void dock_notify(struct ibm_struct *ibm, u32 event) { int docked = dock_docked(); - int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, IBM_PCI_HID); + int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, PCI_ROOT_HID_STRING); if (event == 1 && !pci) /* 570 */ acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */ @@ -2389,7 +2709,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm) acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); - if (ibm_thinkpad_ec_found && experimental) { + if (thinkpad_id.ec_model) { /* * Direct EC access mode: sensors at registers * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for @@ -2533,6 +2853,8 @@ static int thermal_get_sensor(int idx, s32 *value) snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx); if (!acpi_evalf(ec_handle, &t, tmpi, "d")) return -EIO; + if (t > 127 || t < -127) + t = TP_EC_THERMAL_TMP_NA; *value = t * 1000; return 0; } @@ -2671,22 +2993,39 @@ static struct ibm_struct ecdump_driver_data = { * Backlight/brightness subdriver */ -static struct backlight_device *ibm_backlight_device = NULL; +static struct backlight_device *ibm_backlight_device; static struct backlight_ops ibm_backlight_data = { .get_brightness = brightness_get, .update_status = brightness_update_status, }; +static struct mutex brightness_mutex; + static int __init brightness_init(struct ibm_init_struct *iibm) { int b; vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n"); + mutex_init(&brightness_mutex); + + if (!brightness_mode) { + if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) + brightness_mode = 2; + else + brightness_mode = 3; + + dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n", + brightness_mode); + } + + if (brightness_mode > 3) + return -EINVAL; + b = brightness_get(NULL); if (b < 0) - return b; + return 1; ibm_backlight_device = backlight_device_register( TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, @@ -2722,34 +3061,79 @@ static int brightness_update_status(struct backlight_device *bd) bd->props.brightness : 0); } +/* + * ThinkPads can read brightness from two places: EC 0x31, or + * CMOS NVRAM byte 0x5E, bits 0-3. + */ static int brightness_get(struct backlight_device *bd) { - u8 level; - if (!acpi_ec_read(brightness_offset, &level)) - return -EIO; + u8 lec = 0, lcmos = 0, level = 0; - level &= 0x7; + if (brightness_mode & 1) { + if (!acpi_ec_read(brightness_offset, &lec)) + return -EIO; + lec &= 7; + level = lec; + }; + if (brightness_mode & 2) { + lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) + & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) + >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; + level = lcmos; + } + + if (brightness_mode == 3 && lec != lcmos) { + printk(IBM_ERR + "CMOS NVRAM (%u) and EC (%u) do not agree " + "on display brightness level\n", + (unsigned int) lcmos, + (unsigned int) lec); + return -EIO; + } return level; } static int brightness_set(int value) { - int cmos_cmd, inc, i; - int current_value = brightness_get(NULL); + int cmos_cmd, inc, i, res; + int current_value; + + if (value > 7) + return -EINVAL; - value &= 7; + res = mutex_lock_interruptible(&brightness_mutex); + if (res < 0) + return res; + + current_value = brightness_get(NULL); + if (current_value < 0) { + res = current_value; + goto errout; + } - cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN; + cmos_cmd = value > current_value ? + TP_CMOS_BRIGHTNESS_UP : + TP_CMOS_BRIGHTNESS_DOWN; inc = value > current_value ? 1 : -1; + + res = 0; for (i = current_value; i != value; i += inc) { - if (issue_thinkpad_cmos_command(cmos_cmd)) - return -EIO; - if (!acpi_ec_write(brightness_offset, i + inc)) - return -EIO; + if ((brightness_mode & 2) && + issue_thinkpad_cmos_command(cmos_cmd)) { + res = -EIO; + goto errout; + } + if ((brightness_mode & 1) && + !acpi_ec_write(brightness_offset, i + inc)) { + res = -EIO; + goto errout;; + } } - return 0; +errout: + mutex_unlock(&brightness_mutex); + return res; } static int brightness_read(char *p) @@ -3273,20 +3657,19 @@ static int __init fan_init(struct ibm_init_struct *iibm) * Enable for TP-1Y (T43), TP-78 (R51e), * TP-76 (R52), TP-70 (T43, R52), which are known * to be buggy. */ - if (fan_control_initial_status == 0x07 && - ibm_thinkpad_ec_found && - ((ibm_thinkpad_ec_found[0] == '1' && - ibm_thinkpad_ec_found[1] == 'Y') || - (ibm_thinkpad_ec_found[0] == '7' && - (ibm_thinkpad_ec_found[1] == '6' || - ibm_thinkpad_ec_found[1] == '8' || - ibm_thinkpad_ec_found[1] == '0')) - )) { - printk(IBM_NOTICE - "fan_init: initial fan status is " - "unknown, assuming it is in auto " - "mode\n"); - tp_features.fan_ctrl_status_undef = 1; + if (fan_control_initial_status == 0x07) { + switch (thinkpad_id.ec_model) { + case 0x5931: /* TP-1Y */ + case 0x3837: /* TP-78 */ + case 0x3637: /* TP-76 */ + case 0x3037: /* TP-70 */ + printk(IBM_NOTICE + "fan_init: initial fan status is " + "unknown, assuming it is in auto " + "mode\n"); + tp_features.fan_ctrl_status_undef = 1; + ;; + } } } else { printk(IBM_ERR @@ -3474,7 +3857,7 @@ static void fan_watchdog_fire(struct work_struct *ignored) static void fan_watchdog_reset(void) { - static int fan_watchdog_active = 0; + static int fan_watchdog_active; if (fan_control_access_mode == TPACPI_FAN_WR_NONE) return; @@ -3877,7 +4260,7 @@ static struct ibm_struct fan_driver_data = { ****************************************************************************/ /* /proc support */ -static struct proc_dir_entry *proc_dir = NULL; +static struct proc_dir_entry *proc_dir; /* Subdriver registry */ static LIST_HEAD(tpacpi_all_drivers); @@ -4020,13 +4403,30 @@ static void ibm_exit(struct ibm_struct *ibm) /* Probing */ -static char *ibm_thinkpad_ec_found = NULL; - -static char* __init check_dmi_for_ec(void) +static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) { struct dmi_device *dev = NULL; char ec_fw_string[18]; + if (!tp) + return; + + memset(tp, 0, sizeof(*tp)); + + if (dmi_name_in_vendors("IBM")) + tp->vendor = PCI_VENDOR_ID_IBM; + else if (dmi_name_in_vendors("LENOVO")) + tp->vendor = PCI_VENDOR_ID_LENOVO; + else + return; + + tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION), + GFP_KERNEL); + if (!tp->bios_version_str) + return; + tp->bios_model = tp->bios_version_str[0] + | (tp->bios_version_str[1] << 8); + /* * ThinkPad T23 or newer, A31 or newer, R50e or newer, * X32 or newer, all Z series; Some models must have an @@ -4040,10 +4440,20 @@ static char* __init check_dmi_for_ec(void) ec_fw_string) == 1) { ec_fw_string[sizeof(ec_fw_string) - 1] = 0; ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; - return kstrdup(ec_fw_string, GFP_KERNEL); + + tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); + tp->ec_model = ec_fw_string[0] + | (ec_fw_string[1] << 8); + break; } } - return NULL; + + tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), + GFP_KERNEL); + if (strnicmp(tp->model_str, "ThinkPad", 8) != 0) { + kfree(tp->model_str); + tp->model_str = NULL; + } } static int __init probe_for_thinkpad(void) @@ -4057,7 +4467,7 @@ static int __init probe_for_thinkpad(void) * Non-ancient models have better DMI tagging, but very old models * don't. */ - is_thinkpad = dmi_name_in_vendors("ThinkPad"); + is_thinkpad = (thinkpad_id.model_str != NULL); /* ec is required because many other handles are relative to it */ IBM_ACPIHANDLE_INIT(ec); @@ -4073,7 +4483,7 @@ static int __init probe_for_thinkpad(void) * false positives a damn great deal */ if (!is_thinkpad) - is_thinkpad = dmi_name_in_vendors("IBM"); + is_thinkpad = (thinkpad_id.vendor == PCI_VENDOR_ID_IBM); if (!is_thinkpad && !force_load) return -ENODEV; @@ -4185,10 +4595,13 @@ static u32 dbg_level; module_param_named(debug, dbg_level, uint, 0); static int force_load; -module_param(force_load, int, 0); +module_param(force_load, bool, 0); static int fan_control_allowed; -module_param_named(fan_control, fan_control_allowed, int, 0); +module_param_named(fan_control, fan_control_allowed, bool, 0); + +static int brightness_mode; +module_param_named(brightness_mode, brightness_mode, int, 0); #define IBM_PARAM(feature) \ module_param_call(feature, set_ibm_param, NULL, NULL, 0) @@ -4216,12 +4629,16 @@ static int __init thinkpad_acpi_module_init(void) int ret, i; /* Driver-level probe */ + + get_thinkpad_model_data(&thinkpad_id); ret = probe_for_thinkpad(); - if (ret) + if (ret) { + thinkpad_acpi_module_exit(); return ret; + } /* Driver initialization */ - ibm_thinkpad_ec_found = check_dmi_for_ec(); + IBM_ACPIHANDLE_INIT(ecrd); IBM_ACPIHANDLE_INIT(ecwr); @@ -4265,6 +4682,22 @@ static int __init thinkpad_acpi_module_init(void) thinkpad_acpi_module_exit(); return ret; } + tpacpi_inputdev = input_allocate_device(); + if (!tpacpi_inputdev) { + printk(IBM_ERR "unable to allocate input device\n"); + thinkpad_acpi_module_exit(); + return -ENOMEM; + } else { + /* Prepare input device, but don't register */ + tpacpi_inputdev->name = "ThinkPad Extra Buttons"; + tpacpi_inputdev->phys = IBM_DRVR_NAME "/input0"; + tpacpi_inputdev->id.bustype = BUS_HOST; + tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ? + thinkpad_id.vendor : + PCI_VENDOR_ID_IBM; + tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; + tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; + } for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { ret = ibm_init(&ibms_init[i]); if (ret >= 0 && *ibms_init[i].param) @@ -4274,6 +4707,14 @@ static int __init thinkpad_acpi_module_init(void) return ret; } } + ret = input_register_device(tpacpi_inputdev); + if (ret < 0) { + printk(IBM_ERR "unable to register input device\n"); + thinkpad_acpi_module_exit(); + return ret; + } else { + tp_features.input_device_registered = 1; + } return 0; } @@ -4290,6 +4731,13 @@ static void thinkpad_acpi_module_exit(void) dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n"); + if (tpacpi_inputdev) { + if (tp_features.input_device_registered) + input_unregister_device(tpacpi_inputdev); + else + input_free_device(tpacpi_inputdev); + } + if (tpacpi_hwmon) hwmon_device_unregister(tpacpi_hwmon); @@ -4302,7 +4750,9 @@ static void thinkpad_acpi_module_exit(void) if (proc_dir) remove_proc_entry(IBM_PROC_DIR, acpi_root_dir); - kfree(ibm_thinkpad_ec_found); + kfree(thinkpad_id.bios_version_str); + kfree(thinkpad_id.ec_version_str); + kfree(thinkpad_id.model_str); } module_init(thinkpad_acpi_module_init); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 72d62f2dabb..b7a4a888cc8 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -32,6 +32,7 @@ #include <linux/list.h> #include <linux/mutex.h> +#include <linux/nvram.h> #include <linux/proc_fs.h> #include <linux/sysfs.h> #include <linux/backlight.h> @@ -39,6 +40,7 @@ #include <linux/platform_device.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> +#include <linux/input.h> #include <asm/uaccess.h> #include <linux/dmi.h> @@ -48,6 +50,7 @@ #include <acpi/acpi_drivers.h> #include <acpi/acnamesp.h> +#include <linux/pci_ids.h> /**************************************************************************** * Main driver @@ -78,6 +81,11 @@ #define TP_CMOS_BRIGHTNESS_UP 4 #define TP_CMOS_BRIGHTNESS_DOWN 5 +/* ThinkPad CMOS NVRAM constants */ +#define TP_NVRAM_ADDR_BRIGHTNESS 0x5e +#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x07 +#define TP_NVRAM_POS_LEVEL_BRIGHTNESS 0 + #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") #define strlencmp(a,b) (strncmp((a), (b), strlen(b))) @@ -98,9 +106,13 @@ static const char *str_supported(int is_supported); #define vdbg_printk(a_dbg_level, format, arg...) #endif +/* Input IDs */ +#define TPACPI_HKEY_INPUT_VENDOR PCI_VENDOR_ID_IBM +#define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */ +#define TPACPI_HKEY_INPUT_VERSION 0x4101 + /* ACPI HIDs */ #define IBM_HKEY_HID "IBM0068" -#define IBM_PCI_HID "PNP0A03" /* ACPI helpers */ static int __must_check acpi_evalf(acpi_handle handle, @@ -161,6 +173,7 @@ static int parse_strtoul(const char *buf, unsigned long max, static struct platform_device *tpacpi_pdev; static struct class_device *tpacpi_hwmon; static struct platform_driver tpacpi_pdriver; +static struct input_dev *tpacpi_inputdev; static int tpacpi_create_driver_attributes(struct device_driver *drv); static void tpacpi_remove_driver_attributes(struct device_driver *drv); @@ -168,9 +181,7 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv); static int experimental; static u32 dbg_level; static int force_load; -static char *ibm_thinkpad_ec_found; -static char* check_dmi_for_ec(void); static int thinkpad_acpi_module_init(void); static void thinkpad_acpi_module_exit(void); @@ -197,6 +208,7 @@ struct ibm_struct { int (*read) (char *); int (*write) (char *); void (*exit) (void); + void (*resume) (void); struct list_head all_drivers; @@ -228,12 +240,29 @@ static struct { u16 bluetooth:1; u16 hotkey:1; u16 hotkey_mask:1; + u16 hotkey_wlsw:1; u16 light:1; u16 light_status:1; u16 wan:1; u16 fan_ctrl_status_undef:1; + u16 input_device_registered:1; } tp_features; +struct thinkpad_id_data { + unsigned int vendor; /* ThinkPad vendor: + * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */ + + char *bios_version_str; /* Something like 1ZET51WW (1.03z) */ + char *ec_version_str; /* Something like 1ZHT51WW-1.04a */ + + u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */ + u16 ec_model; + + char *model_str; +}; + +static struct thinkpad_id_data thinkpad_id; + static struct list_head tpacpi_all_drivers; static struct ibm_init_struct ibms_init[]; @@ -300,6 +329,7 @@ static int bluetooth_write(char *buf); static struct backlight_device *ibm_backlight_device; static int brightness_offset = 0x31; +static int brightness_mode; static int brightness_init(struct ibm_init_struct *iibm); static void brightness_exit(void); @@ -415,14 +445,14 @@ static int fan_write_cmd_watchdog(const char *cmd, int *rc); */ static int hotkey_orig_status; -static int hotkey_orig_mask; +static u32 hotkey_orig_mask; static struct mutex hotkey_mutex; static int hotkey_init(struct ibm_init_struct *iibm); static void hotkey_exit(void); -static int hotkey_get(int *status, int *mask); -static int hotkey_set(int status, int mask); +static int hotkey_get(int *status, u32 *mask); +static int hotkey_set(int status, u32 mask); static void hotkey_notify(struct ibm_struct *ibm, u32 event); static int hotkey_read(char *p); static int hotkey_write(char *buf); diff --git a/drivers/net/arm/ether1.c b/drivers/net/arm/ether1.c index f21148e7b57..80f33b6d571 100644 --- a/drivers/net/arm/ether1.c +++ b/drivers/net/arm/ether1.c @@ -36,7 +36,6 @@ #include <linux/types.h> #include <linux/fcntl.h> #include <linux/interrupt.h> -#include <linux/ptrace.h> #include <linux/ioport.h> #include <linux/in.h> #include <linux/slab.h> @@ -75,7 +74,7 @@ static void ether1_timeout(struct net_device *dev); /* ------------------------------------------------------------------------- */ -static char version[] __initdata = "ether1 ethernet driver (c) 2000 Russell King v1.07\n"; +static char version[] __devinitdata = "ether1 ethernet driver (c) 2000 Russell King v1.07\n"; #define BUS_16 16 #define BUS_8 8 diff --git a/drivers/net/arm/ether3.c b/drivers/net/arm/ether3.c index a7cac695a9b..3805506a3ab 100644 --- a/drivers/net/arm/ether3.c +++ b/drivers/net/arm/ether3.c @@ -51,7 +51,6 @@ #include <linux/types.h> #include <linux/fcntl.h> #include <linux/interrupt.h> -#include <linux/ptrace.h> #include <linux/ioport.h> #include <linux/in.h> #include <linux/slab.h> @@ -69,7 +68,7 @@ #include <asm/ecard.h> #include <asm/io.h> -static char version[] __initdata = "ether3 ethernet driver (c) 1995-2000 R.M.King v1.17\n"; +static char version[] __devinitdata = "ether3 ethernet driver (c) 1995-2000 R.M.King v1.17\n"; #include "ether3.h" diff --git a/drivers/net/arm/etherh.c b/drivers/net/arm/etherh.c index 769ba69451f..0d37d9d1fd7 100644 --- a/drivers/net/arm/etherh.c +++ b/drivers/net/arm/etherh.c @@ -31,7 +31,6 @@ #include <linux/types.h> #include <linux/fcntl.h> #include <linux/interrupt.h> -#include <linux/ptrace.h> #include <linux/ioport.h> #include <linux/in.h> #include <linux/slab.h> diff --git a/drivers/net/mlx4/cmd.c b/drivers/net/mlx4/cmd.c index c1f81a993f5..a9f31753661 100644 --- a/drivers/net/mlx4/cmd.c +++ b/drivers/net/mlx4/cmd.c @@ -246,8 +246,6 @@ void mlx4_cmd_event(struct mlx4_dev *dev, u16 token, u8 status, u64 out_param) context->result = mlx4_status_to_errno(status); context->out_param = out_param; - context->token += priv->cmd.token_mask + 1; - complete(&context->done); } @@ -264,6 +262,7 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param, spin_lock(&cmd->context_lock); BUG_ON(cmd->free_head < 0); context = &cmd->context[cmd->free_head]; + context->token += cmd->token_mask + 1; cmd->free_head = context->next; spin_unlock(&cmd->context_lock); diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index 3c45142c40b..b0198549846 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -1316,7 +1316,7 @@ static struct of_device_id m8xx_pcmcia_match[] = { MODULE_DEVICE_TABLE(of, m8xx_pcmcia_match); static struct of_platform_driver m8xx_pcmcia_driver = { - .name = (char *)driver_name, + .name = driver_name, .match_table = m8xx_pcmcia_match, .probe = m8xx_probe, .remove = m8xx_remove, diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 9726261c367..ab5ec1feaf4 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -1526,15 +1526,12 @@ zfcp_gid_pn_buffers_alloc(struct zfcp_gid_pn_data **gid_pn, mempool_t *pool) * zfcp_gid_pn_buffers_free - free buffers for GID_PN nameserver request * @gid_pn: pointer to struct zfcp_gid_pn_data which has to be freed */ -static void -zfcp_gid_pn_buffers_free(struct zfcp_gid_pn_data *gid_pn) +static void zfcp_gid_pn_buffers_free(struct zfcp_gid_pn_data *gid_pn) { - if ((gid_pn->ct.pool != 0)) + if (gid_pn->ct.pool) mempool_free(gid_pn, gid_pn->ct.pool); else - kfree(gid_pn); - - return; + kfree(gid_pn); } /** diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 22649639230..b36dfc40d9f 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -126,6 +126,7 @@ zfcp_address_to_sg(void *address, struct scatterlist *list) #define ZFCP_MIN_OUTPUT_THRESHOLD 1 /* ignored by QDIO layer */ #define QDIO_SCSI_QFMT 1 /* 1 for FSF */ +#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer)) /********************* FSF SPECIFIC DEFINES *********************************/ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 4e7cb6dc4d3..d8cd75ce2d9 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -1626,7 +1626,7 @@ zfcp_erp_schedule_work(struct zfcp_unit *unit) { struct zfcp_erp_add_work *p; - p = kmalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) { ZFCP_LOG_NORMAL("error: Out of resources. Could not register " "the FCP-LUN 0x%Lx connected to " @@ -1639,7 +1639,6 @@ zfcp_erp_schedule_work(struct zfcp_unit *unit) } zfcp_unit_get(unit); - memset(p, 0, sizeof(*p)); atomic_set_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status); INIT_WORK(&p->work, zfcp_erp_scsi_scan); p->unit = unit; diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 0eb31e162b1..b240800b78d 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1930,7 +1930,7 @@ static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req) skip_fsfstatus: send_els->status = retval; - if (send_els->handler != 0) + if (send_els->handler) send_els->handler(send_els->handler_data); return retval; diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index bdf5782b8a7..c408badd2ae 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -47,103 +47,56 @@ static int zfcp_qdio_handler_error_check(struct zfcp_adapter *, #define ZFCP_LOG_AREA ZFCP_LOG_AREA_QDIO /* - * Allocates BUFFER memory to each of the pointers of the qdio_buffer_t - * array in the adapter struct. - * Cur_buf is the pointer array and count can be any number of required - * buffers, the page-fitting arithmetic is done entirely within this funciton. + * Frees BUFFER memory for each of the pointers of the struct qdio_buffer array + * in the adapter struct sbuf is the pointer array. * - * returns: number of buffers allocated * locks: must only be called with zfcp_data.config_sema taken */ -static int -zfcp_qdio_buffers_enqueue(struct qdio_buffer **cur_buf, int count) +static void +zfcp_qdio_buffers_dequeue(struct qdio_buffer **sbuf) { - int buf_pos; - int qdio_buffers_per_page; - int page_pos = 0; - struct qdio_buffer *first_in_page = NULL; - - qdio_buffers_per_page = PAGE_SIZE / sizeof (struct qdio_buffer); - ZFCP_LOG_TRACE("buffers_per_page=%d\n", qdio_buffers_per_page); - - for (buf_pos = 0; buf_pos < count; buf_pos++) { - if (page_pos == 0) { - cur_buf[buf_pos] = (struct qdio_buffer *) - get_zeroed_page(GFP_KERNEL); - if (cur_buf[buf_pos] == NULL) { - ZFCP_LOG_INFO("error: allocation of " - "QDIO buffer failed \n"); - goto out; - } - first_in_page = cur_buf[buf_pos]; - } else { - cur_buf[buf_pos] = first_in_page + page_pos; + int pos; - } - /* was initialised to zero */ - page_pos++; - page_pos %= qdio_buffers_per_page; - } - out: - return buf_pos; + for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE) + free_page((unsigned long) sbuf[pos]); } /* - * Frees BUFFER memory for each of the pointers of the struct qdio_buffer array - * in the adapter struct cur_buf is the pointer array and count can be any - * number of buffers in the array that should be freed starting from buffer 0 + * Allocates BUFFER memory to each of the pointers of the qdio_buffer_t + * array in the adapter struct. + * Cur_buf is the pointer array * + * returns: zero on success else -ENOMEM * locks: must only be called with zfcp_data.config_sema taken */ -static void -zfcp_qdio_buffers_dequeue(struct qdio_buffer **cur_buf, int count) +static int +zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbuf) { - int buf_pos; - int qdio_buffers_per_page; - - qdio_buffers_per_page = PAGE_SIZE / sizeof (struct qdio_buffer); - ZFCP_LOG_TRACE("buffers_per_page=%d\n", qdio_buffers_per_page); + int pos; - for (buf_pos = 0; buf_pos < count; buf_pos += qdio_buffers_per_page) - free_page((unsigned long) cur_buf[buf_pos]); - return; + for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE) { + sbuf[pos] = (struct qdio_buffer *) get_zeroed_page(GFP_KERNEL); + if (!sbuf[pos]) { + zfcp_qdio_buffers_dequeue(sbuf); + return -ENOMEM; + } + } + for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos++) + if (pos % QBUFF_PER_PAGE) + sbuf[pos] = sbuf[pos - 1] + 1; + return 0; } /* locks: must only be called with zfcp_data.config_sema taken */ int zfcp_qdio_allocate_queues(struct zfcp_adapter *adapter) { - int buffer_count; - int retval = 0; + int ret; - buffer_count = - zfcp_qdio_buffers_enqueue(&(adapter->request_queue.buffer[0]), - QDIO_MAX_BUFFERS_PER_Q); - if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) { - ZFCP_LOG_DEBUG("only %d QDIO buffers allocated for request " - "queue\n", buffer_count); - zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]), - buffer_count); - retval = -ENOMEM; - goto out; - } - - buffer_count = - zfcp_qdio_buffers_enqueue(&(adapter->response_queue.buffer[0]), - QDIO_MAX_BUFFERS_PER_Q); - if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) { - ZFCP_LOG_DEBUG("only %d QDIO buffers allocated for response " - "queue", buffer_count); - zfcp_qdio_buffers_dequeue(&(adapter->response_queue.buffer[0]), - buffer_count); - ZFCP_LOG_TRACE("freeing request_queue buffers\n"); - zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]), - QDIO_MAX_BUFFERS_PER_Q); - retval = -ENOMEM; - goto out; - } - out: - return retval; + ret = zfcp_qdio_buffers_enqueue(adapter->request_queue.buffer); + if (ret) + return ret; + return zfcp_qdio_buffers_enqueue(adapter->response_queue.buffer); } /* locks: must only be called with zfcp_data.config_sema taken */ @@ -151,12 +104,10 @@ void zfcp_qdio_free_queues(struct zfcp_adapter *adapter) { ZFCP_LOG_TRACE("freeing request_queue buffers\n"); - zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]), - QDIO_MAX_BUFFERS_PER_Q); + zfcp_qdio_buffers_dequeue(adapter->request_queue.buffer); ZFCP_LOG_TRACE("freeing response_queue buffers\n"); - zfcp_qdio_buffers_dequeue(&(adapter->response_queue.buffer[0]), - QDIO_MAX_BUFFERS_PER_Q); + zfcp_qdio_buffers_dequeue(adapter->response_queue.buffer); } int diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index 6b49f6a2524..efd9d8d3a89 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -4,7 +4,7 @@ Written By: Adam Radford <linuxraid@amcc.com> Modifications By: Tom Couch <linuxraid@amcc.com> - Copyright (C) 2004-2006 Applied Micro Circuits Corporation. + Copyright (C) 2004-2007 Applied Micro Circuits 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 @@ -69,6 +69,8 @@ 2.26.02.008 - Free irq handler in __twa_shutdown(). Serialize reset code. Add support for 9650SE controllers. + 2.26.02.009 - Fix dma mask setting to fallback to 32-bit if 64-bit fails. + 2.26.02.010 - Add support for 9690SA controllers. */ #include <linux/module.h> @@ -92,7 +94,7 @@ #include "3w-9xxx.h" /* Globals */ -#define TW_DRIVER_VERSION "2.26.02.008" +#define TW_DRIVER_VERSION "2.26.02.010" static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT]; static unsigned int twa_device_extension_count; static int twa_major = -1; @@ -124,11 +126,11 @@ static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits, unsigned short *fw_on_ctlr_branch, unsigned short *fw_on_ctlr_build, u32 *init_connect_result); -static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length); +static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length); static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds); static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds); static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal); -static int twa_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset); +static int twa_reset_device_extension(TW_Device_Extension *tw_dev); static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset); static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry *sglistarg); static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id); @@ -683,7 +685,7 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int full_command_packet = &tw_ioctl->firmware_command; /* Load request id and sglist for both command types */ - twa_load_sgl(full_command_packet, request_id, dma_handle, data_buffer_length_adjusted); + twa_load_sgl(tw_dev, full_command_packet, request_id, dma_handle, data_buffer_length_adjusted); memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full)); @@ -700,10 +702,10 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { /* Now we need to reset the board */ printk(KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n", - tw_dev->host->host_no, TW_DRIVER, 0xc, + tw_dev->host->host_no, TW_DRIVER, 0x37, cmd); retval = TW_IOCTL_ERROR_OS_EIO; - twa_reset_device_extension(tw_dev, 1); + twa_reset_device_extension(tw_dev); goto out3; } @@ -890,7 +892,9 @@ static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value) } if (status_reg_value & TW_STATUS_QUEUE_ERROR) { - if ((tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9650SE) || (!test_bit(TW_IN_RESET, &tw_dev->flags))) + if (((tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9650SE) && + (tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9690SA)) || + (!test_bit(TW_IN_RESET, &tw_dev->flags))) TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing"); writel(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev)); } @@ -935,8 +939,7 @@ static int twa_empty_response_queue_large(TW_Device_Extension *tw_dev) unsigned long before; int retval = 1; - if ((tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9550SX) || - (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE)) { + if (tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9000) { before = jiffies; while ((response_que_value & TW_9550SX_DRAIN_COMPLETED) != TW_9550SX_DRAIN_COMPLETED) { response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR_LARGE(tw_dev)); @@ -1195,7 +1198,6 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance) u32 status_reg_value; TW_Response_Queue response_que; TW_Command_Full *full_command_packet; - TW_Command *command_packet; TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance; int handled = 0; @@ -1273,7 +1275,6 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance) request_id = TW_RESID_OUT(response_que.response_id); full_command_packet = tw_dev->command_packet_virt[request_id]; error = 0; - command_packet = &full_command_packet->command.oldcommand; /* Check for command packet errors */ if (full_command_packet->command.newcommand.status != 0) { if (tw_dev->srb[request_id] != 0) { @@ -1352,11 +1353,15 @@ twa_interrupt_bail: } /* End twa_interrupt() */ /* This function will load the request id and various sgls for ioctls */ -static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length) +static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length) { TW_Command *oldcommand; TW_Command_Apache *newcommand; TW_SG_Entry *sgl; + unsigned int pae = 0; + + if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4)) + pae = 1; if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) { newcommand = &full_command_packet->command.newcommand; @@ -1372,12 +1377,14 @@ static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, d if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) { /* Load the sg list */ - sgl = (TW_SG_Entry *)((u32 *)oldcommand+TW_SGL_OUT(oldcommand->opcode__sgloffset)); + if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9690SA) + sgl = (TW_SG_Entry *)((u32 *)oldcommand+oldcommand->size - (sizeof(TW_SG_Entry)/4) + pae); + else + sgl = (TW_SG_Entry *)((u32 *)oldcommand+TW_SGL_OUT(oldcommand->opcode__sgloffset)); sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1); sgl->length = cpu_to_le32(length); - if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4)) - oldcommand->size += 1; + oldcommand->size += pae; } } } /* End twa_load_sgl() */ @@ -1506,7 +1513,8 @@ static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, command_que_value = tw_dev->command_packet_phys[request_id]; /* For 9650SE write low 4 bytes first */ - if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) { + if ((tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) || + (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9690SA)) { command_que_value += TW_COMMAND_OFFSET; writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR_LARGE(tw_dev)); } @@ -1537,7 +1545,8 @@ static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, TW_UNMASK_COMMAND_INTERRUPT(tw_dev); goto out; } else { - if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) { + if ((tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) || + (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9690SA)) { /* Now write upper 4 bytes */ writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR_LARGE(tw_dev) + 0x4); } else { @@ -1561,7 +1570,7 @@ out: } /* End twa_post_command_packet() */ /* This function will reset a device extension */ -static int twa_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset) +static int twa_reset_device_extension(TW_Device_Extension *tw_dev) { int i = 0; int retval = 1; @@ -1719,7 +1728,7 @@ static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt) mutex_lock(&tw_dev->ioctl_lock); /* Now reset the card and some of the device extension data */ - if (twa_reset_device_extension(tw_dev, 0)) { + if (twa_reset_device_extension(tw_dev)) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2b, "Controller reset failed during scsi host reset"); goto out; } @@ -2001,11 +2010,14 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id pci_set_master(pdev); - retval = pci_set_dma_mask(pdev, sizeof(dma_addr_t) > 4 ? DMA_64BIT_MASK : DMA_32BIT_MASK); - if (retval) { - TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask"); - goto out_disable_device; - } + if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) + || pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK)) + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) + || pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { + TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask"); + retval = -ENODEV; + goto out_disable_device; + } host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension)); if (!host) { @@ -2053,7 +2065,8 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id goto out_iounmap; /* Set host specific parameters */ - if (pdev->device == PCI_DEVICE_ID_3WARE_9650SE) + if ((pdev->device == PCI_DEVICE_ID_3WARE_9650SE) || + (pdev->device == PCI_DEVICE_ID_3WARE_9690SA)) host->max_id = TW_MAX_UNITS_9650SE; else host->max_id = TW_MAX_UNITS; @@ -2160,6 +2173,8 @@ static struct pci_device_id twa_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9650SE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9690SA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { } }; MODULE_DEVICE_TABLE(pci, twa_pci_tbl); diff --git a/drivers/scsi/3w-9xxx.h b/drivers/scsi/3w-9xxx.h index 7901517d451..d14a9479e38 100644 --- a/drivers/scsi/3w-9xxx.h +++ b/drivers/scsi/3w-9xxx.h @@ -4,7 +4,7 @@ Written By: Adam Radford <linuxraid@amcc.com> Modifications By: Tom Couch <linuxraid@amcc.com> - Copyright (C) 2004-2006 Applied Micro Circuits Corporation. + Copyright (C) 2004-2007 Applied Micro Circuits 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 @@ -419,6 +419,9 @@ static twa_message_type twa_error_table[] = { #ifndef PCI_DEVICE_ID_3WARE_9650SE #define PCI_DEVICE_ID_3WARE_9650SE 0x1004 #endif +#ifndef PCI_DEVICE_ID_3WARE_9690SA +#define PCI_DEVICE_ID_3WARE_9690SA 0x1005 +#endif /* Bitmask macros to eliminate bitfields */ diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index a947257b896..d2b3898b750 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -282,7 +282,7 @@ config SCSI_ISCSI_ATTRS config SCSI_SAS_ATTRS tristate "SAS Transport Attributes" - depends on SCSI + depends on SCSI && BLK_DEV_BSG help If you wish to export transport-specific information about each attached SAS device to sysfs, say Y. @@ -291,8 +291,12 @@ source "drivers/scsi/libsas/Kconfig" endmenu -menu "SCSI low-level drivers" +menuconfig SCSI_LOWLEVEL + bool "SCSI low-level drivers" depends on SCSI!=n + default y + +if SCSI_LOWLEVEL config ISCSI_TCP tristate "iSCSI Initiator over TCP/IP" @@ -1800,7 +1804,7 @@ config SCSI_SRP To compile this driver as a module, choose M here: the module will be called libsrp. -endmenu +endif # SCSI_LOWLEVEL source "drivers/scsi/pcmcia/Kconfig" diff --git a/drivers/scsi/a4000t.c b/drivers/scsi/a4000t.c index 6a5784683ed..0c758d1452b 100644 --- a/drivers/scsi/a4000t.c +++ b/drivers/scsi/a4000t.c @@ -79,6 +79,7 @@ static int __devinit a4000t_probe(struct device *dev) goto out_put_host; } + dev_set_drvdata(dev, host); scsi_scan_host(host); return 0; @@ -95,7 +96,7 @@ static int __devinit a4000t_probe(struct device *dev) static __devexit int a4000t_device_remove(struct device *dev) { - struct Scsi_Host *host = dev_to_shost(dev); + struct Scsi_Host *host = dev_get_drvdata(dev); struct NCR_700_Host_Parameters *hostdata = shost_priv(host); scsi_remove_host(host); diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 0b6fd0b654d..a26baab09db 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -751,6 +751,101 @@ static void setinqstr(struct aac_dev *dev, void *data, int tindex) inqstrcpy ("V1.0", str->prl); } +static void get_container_serial_callback(void *context, struct fib * fibptr) +{ + struct aac_get_serial_resp * get_serial_reply; + struct scsi_cmnd * scsicmd; + + BUG_ON(fibptr == NULL); + + scsicmd = (struct scsi_cmnd *) context; + if (!aac_valid_context(scsicmd, fibptr)) + return; + + get_serial_reply = (struct aac_get_serial_resp *) fib_data(fibptr); + /* Failure is irrelevant, using default value instead */ + if (le32_to_cpu(get_serial_reply->status) == CT_OK) { + char sp[13]; + /* EVPD bit set */ + sp[0] = INQD_PDT_DA; + sp[1] = scsicmd->cmnd[2]; + sp[2] = 0; + sp[3] = snprintf(sp+4, sizeof(sp)-4, "%08X", + le32_to_cpu(get_serial_reply->uid)); + aac_internal_transfer(scsicmd, sp, 0, sizeof(sp)); + } + + scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; + + aac_fib_complete(fibptr); + aac_fib_free(fibptr); + scsicmd->scsi_done(scsicmd); +} + +/** + * aac_get_container_serial - get container serial, none blocking. + */ +static int aac_get_container_serial(struct scsi_cmnd * scsicmd) +{ + int status; + struct aac_get_serial *dinfo; + struct fib * cmd_fibcontext; + struct aac_dev * dev; + + dev = (struct aac_dev *)scsicmd->device->host->hostdata; + + if (!(cmd_fibcontext = aac_fib_alloc(dev))) + return -ENOMEM; + + aac_fib_init(cmd_fibcontext); + dinfo = (struct aac_get_serial *) fib_data(cmd_fibcontext); + + dinfo->command = cpu_to_le32(VM_ContainerConfig); + dinfo->type = cpu_to_le32(CT_CID_TO_32BITS_UID); + dinfo->cid = cpu_to_le32(scmd_id(scsicmd)); + + status = aac_fib_send(ContainerCommand, + cmd_fibcontext, + sizeof (struct aac_get_serial), + FsaNormal, + 0, 1, + (fib_callback) get_container_serial_callback, + (void *) scsicmd); + + /* + * Check that the command queued to the controller + */ + if (status == -EINPROGRESS) { + scsicmd->SCp.phase = AAC_OWNER_FIRMWARE; + return 0; + } + + printk(KERN_WARNING "aac_get_container_serial: aac_fib_send failed with status: %d.\n", status); + aac_fib_complete(cmd_fibcontext); + aac_fib_free(cmd_fibcontext); + return -1; +} + +/* Function: setinqserial + * + * Arguments: [1] pointer to void [1] int + * + * Purpose: Sets SCSI Unit Serial number. + * This is a fake. We should read a proper + * serial number from the container. <SuSE>But + * without docs it's quite hard to do it :-) + * So this will have to do in the meantime.</SuSE> + */ + +static int setinqserial(struct aac_dev *dev, void *data, int cid) +{ + /* + * This breaks array migration. + */ + return snprintf((char *)(data), sizeof(struct scsi_inq) - 4, "%08X%02X", + le32_to_cpu(dev->adapter_info.serial[0]), cid); +} + static void set_sense(u8 *sense_buf, u8 sense_key, u8 sense_code, u8 a_sense_code, u8 incorrect_length, u8 bit_pointer, u16 field_pointer, @@ -1798,6 +1893,49 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) dprintk((KERN_DEBUG "INQUIRY command, ID: %d.\n", cid)); memset(&inq_data, 0, sizeof (struct inquiry_data)); + if (scsicmd->cmnd[1] & 0x1 ) { + char *arr = (char *)&inq_data; + + /* EVPD bit set */ + arr[0] = (scmd_id(scsicmd) == host->this_id) ? + INQD_PDT_PROC : INQD_PDT_DA; + if (scsicmd->cmnd[2] == 0) { + /* supported vital product data pages */ + arr[3] = 2; + arr[4] = 0x0; + arr[5] = 0x80; + arr[1] = scsicmd->cmnd[2]; + aac_internal_transfer(scsicmd, &inq_data, 0, + sizeof(inq_data)); + scsicmd->result = DID_OK << 16 | + COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; + } else if (scsicmd->cmnd[2] == 0x80) { + /* unit serial number page */ + arr[3] = setinqserial(dev, &arr[4], + scmd_id(scsicmd)); + arr[1] = scsicmd->cmnd[2]; + aac_internal_transfer(scsicmd, &inq_data, 0, + sizeof(inq_data)); + return aac_get_container_serial(scsicmd); + } else { + /* vpd page not implemented */ + scsicmd->result = DID_OK << 16 | + COMMAND_COMPLETE << 8 | + SAM_STAT_CHECK_CONDITION; + set_sense((u8 *) &dev->fsa_dev[cid].sense_data, + ILLEGAL_REQUEST, + SENCODE_INVALID_CDB_FIELD, + ASENCODE_NO_SENSE, 0, 7, 2, 0); + memcpy(scsicmd->sense_buffer, + &dev->fsa_dev[cid].sense_data, + (sizeof(dev->fsa_dev[cid].sense_data) > + sizeof(scsicmd->sense_buffer)) + ? sizeof(scsicmd->sense_buffer) + : sizeof(dev->fsa_dev[cid].sense_data)); + } + scsicmd->scsi_done(scsicmd); + return 0; + } inq_data.inqd_ver = 2; /* claim compliance to SCSI-2 */ inq_data.inqd_rdf = 2; /* A response data format value of two indicates that the data shall be in the format specified in SCSI-2 */ inq_data.inqd_len = 31; @@ -2070,7 +2208,7 @@ static int query_disk(struct aac_dev *dev, void __user *arg) } else return -EINVAL; - qd.valid = fsa_dev_ptr[qd.cnum].valid; + qd.valid = fsa_dev_ptr[qd.cnum].valid != 0; qd.locked = fsa_dev_ptr[qd.cnum].locked; qd.deleted = fsa_dev_ptr[qd.cnum].deleted; diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index f1d3b66af87..400d03403cd 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -1567,6 +1567,20 @@ struct aac_get_name_resp { u8 data[16]; }; +#define CT_CID_TO_32BITS_UID 165 +struct aac_get_serial { + __le32 command; /* VM_ContainerConfig */ + __le32 type; /* CT_CID_TO_32BITS_UID */ + __le32 cid; +}; + +struct aac_get_serial_resp { + __le32 dummy0; + __le32 dummy1; + __le32 status; /* CT_OK */ + __le32 uid; +}; + /* * The following command is sent to shut down each container. */ diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index d510839c0bb..bb870906b4c 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -80,7 +80,11 @@ static int fib_map_alloc(struct aac_dev *dev) void aac_fib_map_free(struct aac_dev *dev) { - pci_free_consistent(dev->pdev, dev->max_fib_size * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB), dev->hw_fib_va, dev->hw_fib_pa); + pci_free_consistent(dev->pdev, + dev->max_fib_size * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB), + dev->hw_fib_va, dev->hw_fib_pa); + dev->hw_fib_va = NULL; + dev->hw_fib_pa = 0; } /** @@ -1087,8 +1091,6 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced) * case. */ aac_fib_map_free(aac); - aac->hw_fib_va = NULL; - aac->hw_fib_pa = 0; pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr, aac->comm_phys); aac->comm_addr = NULL; aac->comm_phys = 0; @@ -1098,12 +1100,12 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced) kfree(aac->fsa_dev); aac->fsa_dev = NULL; if (aac_get_driver_ident(index)->quirks & AAC_QUIRK_31BIT) { - if (((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) || - ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_32BIT_MASK)))) + if (((retval = pci_set_dma_mask(aac->pdev, DMA_31BIT_MASK))) || + ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_31BIT_MASK)))) goto out; } else { - if (((retval = pci_set_dma_mask(aac->pdev, 0x7FFFFFFFULL))) || - ((retval = pci_set_consistent_dma_mask(aac->pdev, 0x7FFFFFFFULL)))) + if (((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) || + ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_32BIT_MASK)))) goto out; } if ((retval = (*(aac_get_driver_ident(index)->init))(aac))) diff --git a/drivers/scsi/aic94xx/aic94xx_dev.c b/drivers/scsi/aic94xx/aic94xx_dev.c index c520e5b41fb..3dce618bf41 100644 --- a/drivers/scsi/aic94xx/aic94xx_dev.c +++ b/drivers/scsi/aic94xx/aic94xx_dev.c @@ -126,7 +126,7 @@ static inline int asd_init_sata(struct domain_device *dev) if (w76 & 0x100) /* NCQ? */ qdepth = (w75 & 0x1F) + 1; asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK, - (1<<qdepth)-1); + (1ULL<<qdepth)-1); asd_ddbsite_write_byte(asd_ha, ddb, NUM_SATA_TAGS, qdepth); } if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM || diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index b8c6810090d..ab00aecc546 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -81,6 +81,9 @@ static struct scsi_host_template aic94xx_sht = { .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, + .slave_alloc = sas_slave_alloc, + .target_destroy = sas_target_destroy, + .ioctl = sas_ioctl, }; static int __devinit asd_map_memio(struct asd_ha_struct *asd_ha) diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c index e2ad5bed940..d5d8caba356 100644 --- a/drivers/scsi/aic94xx/aic94xx_task.c +++ b/drivers/scsi/aic94xx/aic94xx_task.c @@ -74,8 +74,13 @@ static inline int asd_map_scatterlist(struct sas_task *task, return 0; } - num_sg = pci_map_sg(asd_ha->pcidev, task->scatter, task->num_scatter, - task->data_dir); + /* STP tasks come from libata which has already mapped + * the SG list */ + if (sas_protocol_ata(task->task_proto)) + num_sg = task->num_scatter; + else + num_sg = pci_map_sg(asd_ha->pcidev, task->scatter, + task->num_scatter, task->data_dir); if (num_sg == 0) return -ENOMEM; @@ -120,8 +125,9 @@ static inline int asd_map_scatterlist(struct sas_task *task, return 0; err_unmap: - pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter, - task->data_dir); + if (sas_protocol_ata(task->task_proto)) + pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter, + task->data_dir); return res; } @@ -142,8 +148,9 @@ static inline void asd_unmap_scatterlist(struct asd_ascb *ascb) } asd_free_coherent(asd_ha, ascb->sg_arr); - pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter, - task->data_dir); + if (task->task_proto != SAS_PROTOCOL_STP) + pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter, + task->data_dir); } /* ---------- Task complete tasklet ---------- */ @@ -391,7 +398,6 @@ static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task, scb->ata_task.total_xfer_len = cpu_to_le32(task->total_xfer_len); scb->ata_task.fis = task->ata_task.fis; - scb->ata_task.fis.fis_type = 0x27; if (likely(!task->ata_task.device_control_reg_update)) scb->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */ scb->ata_task.fis.flags &= 0xF0; /* PM_PORT field shall be 0 */ diff --git a/drivers/scsi/arm/cumana_1.c b/drivers/scsi/arm/cumana_1.c index cf9a21cea6d..49d838e90a2 100644 --- a/drivers/scsi/arm/cumana_1.c +++ b/drivers/scsi/arm/cumana_1.c @@ -24,7 +24,7 @@ #define CUMANASCSI_PUBLIC_RELEASE 1 -#define NCR5380_implementation_fields int port, ctrl +#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata) #define NCR5380_local_declare() struct Scsi_Host *_instance #define NCR5380_setup(instance) _instance = instance #define NCR5380_read(reg) cumanascsi_read(_instance, reg) @@ -33,6 +33,11 @@ #define NCR5380_queue_command cumanascsi_queue_command #define NCR5380_proc_info cumanascsi_proc_info +#define NCR5380_implementation_fields \ + unsigned ctrl; \ + void __iomem *base; \ + void __iomem *dma + #define BOARD_NORMAL 0 #define BOARD_NCR53C400 1 @@ -47,192 +52,162 @@ const char *cumanascsi_info(struct Scsi_Host *spnt) return ""; } -#ifdef NOT_EFFICIENT -#define CTRL(p,v) outb(*ctrl = (v), (p) - 577) -#define STAT(p) inb((p)+1) -#define IN(p) inb((p)) -#define OUT(v,p) outb((v), (p)) -#else -#define CTRL(p,v) (p[-2308] = (*ctrl = (v))) -#define STAT(p) (p[4]) -#define IN(p) (*(p)) -#define IN2(p) ((unsigned short)(*(volatile unsigned long *)(p))) -#define OUT(v,p) (*(p) = (v)) -#define OUT2(v,p) (*((volatile unsigned long *)(p)) = (v)) -#endif -#define L(v) (((v)<<16)|((v) & 0x0000ffff)) -#define H(v) (((v)>>16)|((v) & 0xffff0000)) +#define CTRL 0x16fc +#define STAT 0x2004 +#define L(v) (((v)<<16)|((v) & 0x0000ffff)) +#define H(v) (((v)>>16)|((v) & 0xffff0000)) static inline int -NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, int len) +NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, int len) { - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - int oldctrl = *ctrl; unsigned long *laddr; -#ifdef NOT_EFFICIENT - int iobase = instance->io_port; - int dma_io = iobase & ~(0x3C0000>>2); -#else - volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port); - volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000); -#endif + void __iomem *dma = priv(host)->dma + 0x2000; if(!len) return 0; - CTRL(iobase, 0x02); + writeb(0x02, priv(host)->base + CTRL); laddr = (unsigned long *)addr; while(len >= 32) { - int status; + unsigned int status; unsigned long v; - status = STAT(iobase); + status = readb(priv(host)->base + STAT); if(status & 0x80) goto end; if(!(status & 0x40)) continue; - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; writew(L(v), dma); writew(H(v), dma); + v=*laddr++; writew(L(v), dma); writew(H(v), dma); + v=*laddr++; writew(L(v), dma); writew(H(v), dma); + v=*laddr++; writew(L(v), dma); writew(H(v), dma); + v=*laddr++; writew(L(v), dma); writew(H(v), dma); + v=*laddr++; writew(L(v), dma); writew(H(v), dma); + v=*laddr++; writew(L(v), dma); writew(H(v), dma); + v=*laddr++; writew(L(v), dma); writew(H(v), dma); len -= 32; if(len == 0) break; } addr = (unsigned char *)laddr; - CTRL(iobase, 0x12); + writeb(0x12, priv(host)->base + CTRL); + while(len > 0) { - int status; - status = STAT(iobase); + unsigned int status; + status = readb(priv(host)->base + STAT); if(status & 0x80) goto end; if(status & 0x40) { - OUT(*addr++, dma_io); + writeb(*addr++, dma); if(--len == 0) break; } - status = STAT(iobase); + status = readb(priv(host)->base + STAT); if(status & 0x80) goto end; if(status & 0x40) { - OUT(*addr++, dma_io); + writeb(*addr++, dma); if(--len == 0) break; } } end: - CTRL(iobase, oldctrl|0x40); + writeb(priv(host)->ctrl | 0x40, priv(host)->base + CTRL); return len; } static inline int -NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, int len) +NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, int len) { - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - int oldctrl = *ctrl; unsigned long *laddr; -#ifdef NOT_EFFICIENT - int iobase = instance->io_port; - int dma_io = iobase & ~(0x3C0000>>2); -#else - volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port); - volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000); -#endif + void __iomem *dma = priv(host)->dma + 0x2000; if(!len) return 0; - CTRL(iobase, 0x00); + writeb(0x00, priv(host)->base + CTRL); laddr = (unsigned long *)addr; while(len >= 32) { - int status; - status = STAT(iobase); + unsigned int status; + status = readb(priv(host)->base + STAT); if(status & 0x80) goto end; if(!(status & 0x40)) continue; - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = readw(dma) | (readw(dma) << 16); + *laddr++ = readw(dma) | (readw(dma) << 16); + *laddr++ = readw(dma) | (readw(dma) << 16); + *laddr++ = readw(dma) | (readw(dma) << 16); + *laddr++ = readw(dma) | (readw(dma) << 16); + *laddr++ = readw(dma) | (readw(dma) << 16); + *laddr++ = readw(dma) | (readw(dma) << 16); + *laddr++ = readw(dma) | (readw(dma) << 16); len -= 32; if(len == 0) break; } addr = (unsigned char *)laddr; - CTRL(iobase, 0x10); + writeb(0x10, priv(host)->base + CTRL); + while(len > 0) { - int status; - status = STAT(iobase); + unsigned int status; + status = readb(priv(host)->base + STAT); if(status & 0x80) goto end; if(status & 0x40) { - *addr++ = IN(dma_io); + *addr++ = readb(dma); if(--len == 0) break; } - status = STAT(iobase); + status = readb(priv(host)->base + STAT); if(status & 0x80) goto end; if(status & 0x40) { - *addr++ = IN(dma_io); + *addr++ = readb(dma); if(--len == 0) break; } } end: - CTRL(iobase, oldctrl|0x40); + writeb(priv(host)->ctrl | 0x40, priv(host)->base + CTRL); return len; } -#undef STAT -#undef CTRL -#undef IN -#undef OUT +static unsigned char cumanascsi_read(struct Scsi_Host *host, unsigned int reg) +{ + void __iomem *base = priv(host)->base; + unsigned char val; -#define CTRL(p,v) outb(*ctrl = (v), (p) - 577) + writeb(0, base + CTRL); -static char cumanascsi_read(struct Scsi_Host *instance, int reg) -{ - unsigned int iobase = instance->io_port; - int i; - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; + val = readb(base + 0x2100 + (reg << 2)); - CTRL(iobase, 0); - i = inb(iobase + 64 + reg); - CTRL(iobase, 0x40); + priv(host)->ctrl = 0x40; + writeb(0x40, base + CTRL); - return i; + return val; } -static void cumanascsi_write(struct Scsi_Host *instance, int reg, int value) +static void cumanascsi_write(struct Scsi_Host *host, unsigned int reg, unsigned int value) { - int iobase = instance->io_port; - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; + void __iomem *base = priv(host)->base; - CTRL(iobase, 0); - outb(value, iobase + 64 + reg); - CTRL(iobase, 0x40); -} + writeb(0, base + CTRL); -#undef CTRL + writeb(value, base + 0x2100 + (reg << 2)); + + priv(host)->ctrl = 0x40; + writeb(0x40, base + CTRL); +} #include "../NCR5380.c" @@ -256,32 +231,46 @@ static int __devinit cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id) { struct Scsi_Host *host; - int ret = -ENOMEM; + int ret; - host = scsi_host_alloc(&cumanascsi_template, sizeof(struct NCR5380_hostdata)); - if (!host) + ret = ecard_request_resources(ec); + if (ret) goto out; - host->io_port = ecard_address(ec, ECARD_IOC, ECARD_SLOW) + 0x800; + host = scsi_host_alloc(&cumanascsi_template, sizeof(struct NCR5380_hostdata)); + if (!host) { + ret = -ENOMEM; + goto out_release; + } + + priv(host)->base = ioremap(ecard_resource_start(ec, ECARD_RES_IOCSLOW), + ecard_resource_len(ec, ECARD_RES_IOCSLOW)); + priv(host)->dma = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC), + ecard_resource_len(ec, ECARD_RES_MEMC)); + if (!priv(host)->base || !priv(host)->dma) { + ret = -ENOMEM; + goto out_unmap; + } + host->irq = ec->irq; NCR5380_init(host, 0); + priv(host)->ctrl = 0; + writeb(0, priv(host)->base + CTRL); + host->n_io_port = 255; if (!(request_region(host->io_port, host->n_io_port, "CumanaSCSI-1"))) { ret = -EBUSY; - goto out_free; + goto out_unmap; } - ((struct NCR5380_hostdata *)host->hostdata)->ctrl = 0; - outb(0x00, host->io_port - 577); - ret = request_irq(host->irq, cumanascsi_intr, IRQF_DISABLED, "CumanaSCSI-1", host); if (ret) { printk("scsi%d: IRQ%d not free: %d\n", host->host_no, host->irq, ret); - goto out_release; + goto out_unmap; } printk("scsi%d: at port 0x%08lx irq %d", @@ -301,10 +290,12 @@ cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id) out_free_irq: free_irq(host->irq, host); - out_release: - release_region(host->io_port, host->n_io_port); - out_free: + out_unmap: + iounmap(priv(host)->base); + iounmap(priv(host)->dma); scsi_host_put(host); + out_release: + ecard_release_resources(ec); out: return ret; } @@ -318,8 +309,10 @@ static void __devexit cumanascsi1_remove(struct expansion_card *ec) scsi_remove_host(host); free_irq(host->irq, host); NCR5380_exit(host); - release_region(host->io_port, host->n_io_port); + iounmap(priv(host)->base); + iounmap(priv(host)->dma); scsi_host_put(host); + ecard_release_resources(ec); } static const struct ecard_id cumanascsi1_cids[] = { diff --git a/drivers/scsi/arm/ecoscsi.c b/drivers/scsi/arm/ecoscsi.c index 378e7af0c5d..5265a988433 100644 --- a/drivers/scsi/arm/ecoscsi.c +++ b/drivers/scsi/arm/ecoscsi.c @@ -34,35 +34,25 @@ #include "../scsi.h" #include <scsi/scsi_host.h> -#define NCR5380_implementation_fields int port, ctrl -#define NCR5380_local_declare() struct Scsi_Host *_instance -#define NCR5380_setup(instance) _instance = instance +#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata) -#define NCR5380_read(reg) ecoscsi_read(_instance, reg) -#define NCR5380_write(reg, value) ecoscsi_write(_instance, reg, value) +#define NCR5380_local_declare() void __iomem *_base +#define NCR5380_setup(host) _base = priv(host)->base + +#define NCR5380_read(reg) ({ writeb(reg | 8, _base); readb(_base + 4); }) +#define NCR5380_write(reg, value) ({ writeb(reg | 8, _base); writeb(value, _base + 4); }) #define NCR5380_intr ecoscsi_intr #define NCR5380_queue_command ecoscsi_queue_command #define NCR5380_proc_info ecoscsi_proc_info +#define NCR5380_implementation_fields \ + void __iomem *base + #include "../NCR5380.h" #define ECOSCSI_PUBLIC_RELEASE 1 -static char ecoscsi_read(struct Scsi_Host *instance, int reg) -{ - int iobase = instance->io_port; - outb(reg | 8, iobase); - return inb(iobase + 1); -} - -static void ecoscsi_write(struct Scsi_Host *instance, int reg, int value) -{ - int iobase = instance->io_port; - outb(reg | 8, iobase); - outb(value, iobase + 1); -} - /* * Function : ecoscsi_setup(char *str, int *ints) * @@ -82,73 +72,6 @@ const char * ecoscsi_info (struct Scsi_Host *spnt) return ""; } -#if 0 -#define STAT(p) inw(p + 144) - -static inline int NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, - int len) -{ - int iobase = host->io_port; -printk("writing %p len %d\n",addr, len); - if(!len) return -1; - - while(1) - { - int status; - while(((status = STAT(iobase)) & 0x100)==0); - } -} - -static inline int NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, - int len) -{ - int iobase = host->io_port; - int iobase2= host->io_port + 0x100; - unsigned char *start = addr; - int s; -printk("reading %p len %d\n",addr, len); - outb(inb(iobase + 128), iobase + 135); - while(len > 0) - { - int status,b,i, timeout; - timeout = 0x07FFFFFF; - while(((status = STAT(iobase)) & 0x100)==0) - { - timeout--; - if(status & 0x200 || !timeout) - { - printk("status = %p\n",status); - outb(0, iobase + 135); - return 1; - } - } - if(len >= 128) - { - for(i=0; i<64; i++) - { - b = inw(iobase + 136); - *addr++ = b; - *addr++ = b>>8; - } - len -= 128; - } - else - { - b = inw(iobase + 136); - *addr ++ = b; - len -= 1; - if(len) - *addr ++ = b>>8; - len -= 1; - } - } - outb(0, iobase + 135); - printk("first bytes = %02X %02X %02X %20X %02X %02X %02X\n",*start, start[1], start[2], start[3], start[4], start[5], start[6]); - return 1; -} -#endif -#undef STAT - #define BOARD_NORMAL 0 #define BOARD_NCR53C400 1 @@ -173,25 +96,36 @@ static struct Scsi_Host *host; static int __init ecoscsi_init(void) { + void __iomem *_base; + int ret; - host = scsi_host_alloc(tpnt, sizeof(struct NCR5380_hostdata)); - if (!host) - return 0; + if (!request_mem_region(0x33a0000, 4096, "ecoscsi")) { + ret = -EBUSY; + goto out; + } - host->io_port = 0x80ce8000; - host->n_io_port = 144; - host->irq = IRQ_NONE; + _base = ioremap(0x33a0000, 4096); + if (!_base) { + ret = -ENOMEM; + goto out_release; + } - if (!(request_region(host->io_port, host->n_io_port, "ecoscsi")) ) - goto unregister_scsi; + NCR5380_write(MODE_REG, 0x20); /* Is it really SCSI? */ + if (NCR5380_read(MODE_REG) != 0x20) /* Write to a reg. */ + goto out_unmap; - ecoscsi_write(host, MODE_REG, 0x20); /* Is it really SCSI? */ - if (ecoscsi_read(host, MODE_REG) != 0x20) /* Write to a reg. */ - goto release_reg; + NCR5380_write(MODE_REG, 0x00); /* it back. */ + if (NCR5380_read(MODE_REG) != 0x00) + goto out_unmap; - ecoscsi_write(host, MODE_REG, 0x00 ); /* it back. */ - if (ecoscsi_read(host, MODE_REG) != 0x00) - goto release_reg; + host = scsi_host_alloc(tpnt, sizeof(struct NCR5380_hostdata)); + if (!host) { + ret = -ENOMEM; + goto out_unmap; + } + + priv(host)->base = _base; + host->irq = IRQ_NONE; NCR5380_init(host, 0); @@ -206,24 +140,20 @@ static int __init ecoscsi_init(void) scsi_scan_host(host); return 0; -release_reg: - release_region(host->io_port, host->n_io_port); -unregister_scsi: - scsi_host_put(host); - return -ENODEV; + out_unmap: + iounmap(_base); + out_release: + release_mem_region(0x33a0000, 4096); + out: + return ret; } static void __exit ecoscsi_exit(void) { scsi_remove_host(host); - - if (shpnt->irq != IRQ_NONE) - free_irq(shpnt->irq, NULL); NCR5380_exit(host); - if (shpnt->io_port) - release_region(shpnt->io_port, shpnt->n_io_port); - scsi_host_put(host); + release_mem_region(0x33a0000, 4096); return 0; } diff --git a/drivers/scsi/arm/oak.c b/drivers/scsi/arm/oak.c index c21b8392c92..849cdf89f7b 100644 --- a/drivers/scsi/arm/oak.c +++ b/drivers/scsi/arm/oak.c @@ -23,15 +23,18 @@ #define OAKSCSI_PUBLIC_RELEASE 1 -#define NCR5380_read(reg) oakscsi_read(_instance, reg) -#define NCR5380_write(reg, value) oakscsi_write(_instance, reg, value) +#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata) +#define NCR5380_local_declare() void __iomem *_base +#define NCR5380_setup(host) _base = priv(host)->base + +#define NCR5380_read(reg) readb(_base + ((reg) << 2)) +#define NCR5380_write(reg, value) writeb(value, _base + ((reg) << 2)) #define NCR5380_intr oakscsi_intr #define NCR5380_queue_command oakscsi_queue_command #define NCR5380_proc_info oakscsi_proc_info -#define NCR5380_implementation_fields int port, ctrl -#define NCR5380_local_declare() struct Scsi_Host *_instance -#define NCR5380_setup(instance) _instance = instance +#define NCR5380_implementation_fields \ + void __iomem *base #define BOARD_NORMAL 0 #define BOARD_NCR53C400 1 @@ -39,60 +42,62 @@ #include "../NCR5380.h" #undef START_DMA_INITIATOR_RECEIVE_REG -#define START_DMA_INITIATOR_RECEIVE_REG (7 + 128) +#define START_DMA_INITIATOR_RECEIVE_REG (128 + 7) const char * oakscsi_info (struct Scsi_Host *spnt) { return ""; } -#define STAT(p) inw(p + 144) -extern void inswb(int from, void *to, int len); +#define STAT ((128 + 16) << 2) +#define DATA ((128 + 8) << 2) static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, int len) { - int iobase = instance->io_port; + void __iomem *base = priv(instance)->base; + printk("writing %p len %d\n",addr, len); if(!len) return -1; while(1) { int status; - while(((status = STAT(iobase)) & 0x100)==0); + while (((status = readw(base + STAT)) & 0x100)==0); } } static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, int len) { - int iobase = instance->io_port; + void __iomem *base = priv(instance)->base; printk("reading %p len %d\n", addr, len); while(len > 0) { - int status, timeout; + unsigned int status, timeout; unsigned long b; timeout = 0x01FFFFFF; - while(((status = STAT(iobase)) & 0x100)==0) + while (((status = readw(base + STAT)) & 0x100)==0) { timeout--; if(status & 0x200 || !timeout) { - printk("status = %08X\n",status); + printk("status = %08X\n", status); return 1; } } + if(len >= 128) { - inswb(iobase + 136, addr, 128); + readsw(base + DATA, addr, 128); addr += 128; len -= 128; } else { - b = (unsigned long) inw(iobase + 136); + b = (unsigned long) readw(base + DATA); *addr ++ = b; len -= 1; if(len) @@ -103,10 +108,8 @@ printk("reading %p len %d\n", addr, len); return 0; } -#define oakscsi_read(instance,reg) (inb((instance)->io_port + (reg))) -#define oakscsi_write(instance,reg,val) (outb((val), (instance)->io_port + (reg))) - #undef STAT +#undef DATA #include "../NCR5380.c" @@ -132,18 +135,26 @@ oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id) struct Scsi_Host *host; int ret = -ENOMEM; - host = scsi_host_alloc(&oakscsi_template, sizeof(struct NCR5380_hostdata)); - if (!host) + ret = ecard_request_resources(ec); + if (ret) goto out; - host->io_port = ecard_address(ec, ECARD_MEMC, 0); + host = scsi_host_alloc(&oakscsi_template, sizeof(struct NCR5380_hostdata)); + if (!host) { + ret = -ENOMEM; + goto release; + } + + priv(host)->base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC), + ecard_resource_len(ec, ECARD_RES_MEMC)); + if (!priv(host)->base) { + ret = -ENOMEM; + goto unreg; + } + host->irq = IRQ_NONE; host->n_io_port = 255; - ret = -EBUSY; - if (!request_region (host->io_port, host->n_io_port, "Oak SCSI")) - goto unreg; - NCR5380_init(host, 0); printk("scsi%d: at port 0x%08lx irqs disabled", @@ -156,15 +167,17 @@ oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id) ret = scsi_add_host(host, &ec->dev); if (ret) - goto out_release; + goto out_unmap; scsi_scan_host(host); goto out; - out_release: - release_region(host->io_port, host->n_io_port); + out_unmap: + iounmap(priv(host)->base); unreg: scsi_host_put(host); + release: + ecard_release_resources(ec); out: return ret; } @@ -177,8 +190,9 @@ static void __devexit oakscsi_remove(struct expansion_card *ec) scsi_remove_host(host); NCR5380_exit(host); - release_region(host->io_port, host->n_io_port); + iounmap(priv(host)->base); scsi_host_put(host); + ecard_release_resources(ec); } static const struct ecard_id oakscsi_cids[] = { diff --git a/drivers/scsi/bvme6000_scsi.c b/drivers/scsi/bvme6000_scsi.c index 012cdea7946..cac35408673 100644 --- a/drivers/scsi/bvme6000_scsi.c +++ b/drivers/scsi/bvme6000_scsi.c @@ -74,6 +74,7 @@ bvme6000_probe(struct device *dev) goto out_put_host; } + dev_set_drvdata(dev, host); scsi_scan_host(host); return 0; @@ -89,7 +90,7 @@ bvme6000_probe(struct device *dev) static __devexit int bvme6000_device_remove(struct device *dev) { - struct Scsi_Host *host = dev_to_shost(dev); + struct Scsi_Host *host = dev_get_drvdata(dev); struct NCR_700_Host_Parameters *hostdata = shost_priv(host); scsi_remove_host(host); diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h index d5576d54ce7..856e38b1486 100644 --- a/drivers/scsi/esp_scsi.h +++ b/drivers/scsi/esp_scsi.h @@ -220,7 +220,7 @@ #define ESP_BUSID_RESELID 0x10 #define ESP_BUSID_CTR32BIT 0x40 -#define ESP_BUS_TIMEOUT 250 /* In milli-seconds */ +#define ESP_BUS_TIMEOUT 275 /* In milli-seconds */ #define ESP_TIMEO_CONST 8192 #define ESP_NEG_DEFP(mhz, cfact) \ ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact))) diff --git a/drivers/scsi/libsas/Kconfig b/drivers/scsi/libsas/Kconfig index aafdc92f831..3a3c1ac9c6c 100644 --- a/drivers/scsi/libsas/Kconfig +++ b/drivers/scsi/libsas/Kconfig @@ -30,6 +30,13 @@ config SCSI_SAS_LIBSAS This provides transport specific helpers for SAS drivers which use the domain device construct (like the aic94xxx). +config SCSI_SAS_ATA + bool "ATA support for libsas (requires libata)" + depends on SCSI_SAS_LIBSAS && ATA + help + Builds in ATA support into libsas. Will necessitate + the loading of libata along with libsas. + config SCSI_SAS_LIBSAS_DEBUG bool "Compile the SAS Domain Transport Attributes in debug mode" default y diff --git a/drivers/scsi/libsas/Makefile b/drivers/scsi/libsas/Makefile index 44d972a3b4b..fd387b91856 100644 --- a/drivers/scsi/libsas/Makefile +++ b/drivers/scsi/libsas/Makefile @@ -34,3 +34,4 @@ libsas-y += sas_init.o \ sas_discover.o \ sas_expander.o \ sas_scsi_host.o +libsas-$(CONFIG_SCSI_SAS_ATA) += sas_ata.o diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c new file mode 100644 index 00000000000..ced2de32c51 --- /dev/null +++ b/drivers/scsi/libsas/sas_ata.c @@ -0,0 +1,817 @@ +/* + * Support for SATA devices on Serial Attached SCSI (SAS) controllers + * + * Copyright (C) 2006 IBM Corporation + * + * Written by: Darrick J. Wong <djwong@us.ibm.com>, IBM 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/scatterlist.h> + +#include <scsi/sas_ata.h> +#include "sas_internal.h" +#include <scsi/scsi_host.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi.h> +#include <scsi/scsi_transport.h> +#include <scsi/scsi_transport_sas.h> +#include "../scsi_sas_internal.h" +#include "../scsi_transport_api.h" +#include <scsi/scsi_eh.h> + +static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) +{ + /* Cheesy attempt to translate SAS errors into ATA. Hah! */ + + /* transport error */ + if (ts->resp == SAS_TASK_UNDELIVERED) + return AC_ERR_ATA_BUS; + + /* ts->resp == SAS_TASK_COMPLETE */ + /* task delivered, what happened afterwards? */ + switch (ts->stat) { + case SAS_DEV_NO_RESPONSE: + return AC_ERR_TIMEOUT; + + case SAS_INTERRUPTED: + case SAS_PHY_DOWN: + case SAS_NAK_R_ERR: + return AC_ERR_ATA_BUS; + + + case SAS_DATA_UNDERRUN: + /* + * Some programs that use the taskfile interface + * (smartctl in particular) can cause underrun + * problems. Ignore these errors, perhaps at our + * peril. + */ + return 0; + + case SAS_DATA_OVERRUN: + case SAS_QUEUE_FULL: + case SAS_DEVICE_UNKNOWN: + case SAS_SG_ERR: + return AC_ERR_INVALID; + + case SAM_CHECK_COND: + case SAS_OPEN_TO: + case SAS_OPEN_REJECT: + SAS_DPRINTK("%s: Saw error %d. What to do?\n", + __FUNCTION__, ts->stat); + return AC_ERR_OTHER; + + case SAS_ABORTED_TASK: + return AC_ERR_DEV; + + case SAS_PROTO_RESPONSE: + /* This means the ending_fis has the error + * value; return 0 here to collect it */ + return 0; + default: + return 0; + } +} + +static void sas_ata_task_done(struct sas_task *task) +{ + struct ata_queued_cmd *qc = task->uldd_task; + struct domain_device *dev; + struct task_status_struct *stat = &task->task_status; + struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; + struct sas_ha_struct *sas_ha; + enum ata_completion_errors ac; + unsigned long flags; + + if (!qc) + goto qc_already_gone; + + dev = qc->ap->private_data; + sas_ha = dev->port->ha; + + spin_lock_irqsave(dev->sata_dev.ap->lock, flags); + if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_GOOD) { + ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); + qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); + dev->sata_dev.sstatus = resp->sstatus; + dev->sata_dev.serror = resp->serror; + dev->sata_dev.scontrol = resp->scontrol; + } else if (stat->stat != SAM_STAT_GOOD) { + ac = sas_to_ata_err(stat); + if (ac) { + SAS_DPRINTK("%s: SAS error %x\n", __FUNCTION__, + stat->stat); + /* We saw a SAS error. Send a vague error. */ + qc->err_mask = ac; + dev->sata_dev.tf.feature = 0x04; /* status err */ + dev->sata_dev.tf.command = ATA_ERR; + } + } + + qc->lldd_task = NULL; + if (qc->scsicmd) + ASSIGN_SAS_TASK(qc->scsicmd, NULL); + ata_qc_complete(qc); + spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); + + /* + * If the sas_task has an ata qc, a scsi_cmnd and the aborted + * flag is set, then we must have come in via the libsas EH + * functions. When we exit this function, we need to put the + * scsi_cmnd on the list of finished errors. The ata_qc_complete + * call cleans up the libata side of things but we're protected + * from the scsi_cmnd going away because the scsi_cmnd is owned + * by the EH, making libata's call to scsi_done a NOP. + */ + spin_lock_irqsave(&task->task_state_lock, flags); + if (qc->scsicmd && task->task_state_flags & SAS_TASK_STATE_ABORTED) + scsi_eh_finish_cmd(qc->scsicmd, &sas_ha->eh_done_q); + spin_unlock_irqrestore(&task->task_state_lock, flags); + +qc_already_gone: + list_del_init(&task->list); + sas_free_task(task); +} + +static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) +{ + int res; + struct sas_task *task; + struct domain_device *dev = qc->ap->private_data; + struct sas_ha_struct *sas_ha = dev->port->ha; + struct Scsi_Host *host = sas_ha->core.shost; + struct sas_internal *i = to_sas_internal(host->transportt); + struct scatterlist *sg; + unsigned int num = 0; + unsigned int xfer = 0; + + task = sas_alloc_task(GFP_ATOMIC); + if (!task) + return AC_ERR_SYSTEM; + task->dev = dev; + task->task_proto = SAS_PROTOCOL_STP; + task->task_done = sas_ata_task_done; + + if (qc->tf.command == ATA_CMD_FPDMA_WRITE || + qc->tf.command == ATA_CMD_FPDMA_READ) { + /* Need to zero out the tag libata assigned us */ + qc->tf.nsect = 0; + } + + ata_tf_to_fis(&qc->tf, 1, 0, (u8*)&task->ata_task.fis); + task->uldd_task = qc; + if (is_atapi_taskfile(&qc->tf)) { + memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len); + task->total_xfer_len = qc->nbytes + qc->pad_len; + task->num_scatter = qc->pad_len ? qc->n_elem + 1 : qc->n_elem; + } else { + ata_for_each_sg(sg, qc) { + num++; + xfer += sg->length; + } + + task->total_xfer_len = xfer; + task->num_scatter = num; + } + + task->data_dir = qc->dma_dir; + task->scatter = qc->__sg; + task->ata_task.retry_count = 1; + task->task_state_flags = SAS_TASK_STATE_PENDING; + qc->lldd_task = task; + + switch (qc->tf.protocol) { + case ATA_PROT_NCQ: + task->ata_task.use_ncq = 1; + /* fall through */ + case ATA_PROT_ATAPI_DMA: + case ATA_PROT_DMA: + task->ata_task.dma_xfer = 1; + break; + } + + if (qc->scsicmd) + ASSIGN_SAS_TASK(qc->scsicmd, task); + + if (sas_ha->lldd_max_execute_num < 2) + res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); + else + res = sas_queue_up(task); + + /* Examine */ + if (res) { + SAS_DPRINTK("lldd_execute_task returned: %d\n", res); + + if (qc->scsicmd) + ASSIGN_SAS_TASK(qc->scsicmd, NULL); + sas_free_task(task); + return AC_ERR_SYSTEM; + } + + return 0; +} + +static u8 sas_ata_check_status(struct ata_port *ap) +{ + struct domain_device *dev = ap->private_data; + return dev->sata_dev.tf.command; +} + +static void sas_ata_phy_reset(struct ata_port *ap) +{ + struct domain_device *dev = ap->private_data; + struct sas_internal *i = + to_sas_internal(dev->port->ha->core.shost->transportt); + int res = 0; + + if (i->dft->lldd_I_T_nexus_reset) + res = i->dft->lldd_I_T_nexus_reset(dev); + + if (res) + SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __FUNCTION__); + + switch (dev->sata_dev.command_set) { + case ATA_COMMAND_SET: + SAS_DPRINTK("%s: Found ATA device.\n", __FUNCTION__); + ap->device[0].class = ATA_DEV_ATA; + break; + case ATAPI_COMMAND_SET: + SAS_DPRINTK("%s: Found ATAPI device.\n", __FUNCTION__); + ap->device[0].class = ATA_DEV_ATAPI; + break; + default: + SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", + __FUNCTION__, + dev->sata_dev.command_set); + ap->device[0].class = ATA_DEV_UNKNOWN; + break; + } + + ap->cbl = ATA_CBL_SATA; +} + +static void sas_ata_post_internal(struct ata_queued_cmd *qc) +{ + if (qc->flags & ATA_QCFLAG_FAILED) + qc->err_mask |= AC_ERR_OTHER; + + if (qc->err_mask) { + /* + * Find the sas_task and kill it. By this point, + * libata has decided to kill the qc, so we needn't + * bother with sas_ata_task_done. But we still + * ought to abort the task. + */ + struct sas_task *task = qc->lldd_task; + unsigned long flags; + + qc->lldd_task = NULL; + if (task) { + /* Should this be a AT(API) device reset? */ + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + task->uldd_task = NULL; + __sas_task_abort(task); + } + } +} + +static void sas_ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct domain_device *dev = ap->private_data; + memcpy(tf, &dev->sata_dev.tf, sizeof (*tf)); +} + +static int sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, + u32 val) +{ + struct domain_device *dev = ap->private_data; + + SAS_DPRINTK("STUB %s\n", __FUNCTION__); + switch (sc_reg_in) { + case SCR_STATUS: + dev->sata_dev.sstatus = val; + break; + case SCR_CONTROL: + dev->sata_dev.scontrol = val; + break; + case SCR_ERROR: + dev->sata_dev.serror = val; + break; + case SCR_ACTIVE: + dev->sata_dev.ap->sactive = val; + break; + default: + return -EINVAL; + } + return 0; +} + +static int sas_ata_scr_read(struct ata_port *ap, unsigned int sc_reg_in, + u32 *val) +{ + struct domain_device *dev = ap->private_data; + + SAS_DPRINTK("STUB %s\n", __FUNCTION__); + switch (sc_reg_in) { + case SCR_STATUS: + *val = dev->sata_dev.sstatus; + return 0; + case SCR_CONTROL: + *val = dev->sata_dev.scontrol; + return 0; + case SCR_ERROR: + *val = dev->sata_dev.serror; + return 0; + case SCR_ACTIVE: + *val = dev->sata_dev.ap->sactive; + return 0; + default: + return -EINVAL; + } +} + +static struct ata_port_operations sas_sata_ops = { + .port_disable = ata_port_disable, + .check_status = sas_ata_check_status, + .check_altstatus = sas_ata_check_status, + .dev_select = ata_noop_dev_select, + .phy_reset = sas_ata_phy_reset, + .post_internal_cmd = sas_ata_post_internal, + .tf_read = sas_ata_tf_read, + .qc_prep = ata_noop_qc_prep, + .qc_issue = sas_ata_qc_issue, + .port_start = ata_sas_port_start, + .port_stop = ata_sas_port_stop, + .scr_read = sas_ata_scr_read, + .scr_write = sas_ata_scr_write +}; + +static struct ata_port_info sata_port_info = { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, + .pio_mask = 0x1f, /* PIO0-4 */ + .mwdma_mask = 0x07, /* MWDMA0-2 */ + .udma_mask = ATA_UDMA6, + .port_ops = &sas_sata_ops +}; + +int sas_ata_init_host_and_port(struct domain_device *found_dev, + struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(&starget->dev); + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct ata_port *ap; + + ata_host_init(&found_dev->sata_dev.ata_host, + &ha->pcidev->dev, + sata_port_info.flags, + &sas_sata_ops); + ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host, + &sata_port_info, + shost); + if (!ap) { + SAS_DPRINTK("ata_sas_port_alloc failed.\n"); + return -ENODEV; + } + + ap->private_data = found_dev; + ap->cbl = ATA_CBL_SATA; + ap->scsi_host = shost; + found_dev->sata_dev.ap = ap; + + return 0; +} + +void sas_ata_task_abort(struct sas_task *task) +{ + struct ata_queued_cmd *qc = task->uldd_task; + struct completion *waiting; + + /* Bounce SCSI-initiated commands to the SCSI EH */ + if (qc->scsicmd) { + scsi_req_abort_cmd(qc->scsicmd); + scsi_schedule_eh(qc->scsicmd->device->host); + return; + } + + /* Internal command, fake a timeout and complete. */ + qc->flags &= ~ATA_QCFLAG_ACTIVE; + qc->flags |= ATA_QCFLAG_FAILED; + qc->err_mask |= AC_ERR_TIMEOUT; + waiting = qc->private_data; + complete(waiting); +} + +static void sas_task_timedout(unsigned long _task) +{ + struct sas_task *task = (void *) _task; + unsigned long flags; + + spin_lock_irqsave(&task->task_state_lock, flags); + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + complete(&task->completion); +} + +static void sas_disc_task_done(struct sas_task *task) +{ + if (!del_timer(&task->timer)) + return; + complete(&task->completion); +} + +#define SAS_DEV_TIMEOUT 10 + +/** + * sas_execute_task -- Basic task processing for discovery + * @task: the task to be executed + * @buffer: pointer to buffer to do I/O + * @size: size of @buffer + * @pci_dma_dir: PCI_DMA_... + */ +static int sas_execute_task(struct sas_task *task, void *buffer, int size, + int pci_dma_dir) +{ + int res = 0; + struct scatterlist *scatter = NULL; + struct task_status_struct *ts = &task->task_status; + int num_scatter = 0; + int retries = 0; + struct sas_internal *i = + to_sas_internal(task->dev->port->ha->core.shost->transportt); + + if (pci_dma_dir != PCI_DMA_NONE) { + scatter = kzalloc(sizeof(*scatter), GFP_KERNEL); + if (!scatter) + goto out; + + sg_init_one(scatter, buffer, size); + num_scatter = 1; + } + + task->task_proto = task->dev->tproto; + task->scatter = scatter; + task->num_scatter = num_scatter; + task->total_xfer_len = size; + task->data_dir = pci_dma_dir; + task->task_done = sas_disc_task_done; + if (pci_dma_dir != PCI_DMA_NONE && + sas_protocol_ata(task->task_proto)) { + task->num_scatter = pci_map_sg(task->dev->port->ha->pcidev, + task->scatter, + task->num_scatter, + task->data_dir); + } + + for (retries = 0; retries < 5; retries++) { + task->task_state_flags = SAS_TASK_STATE_PENDING; + init_completion(&task->completion); + + task->timer.data = (unsigned long) task; + task->timer.function = sas_task_timedout; + task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ; + add_timer(&task->timer); + + res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); + if (res) { + del_timer(&task->timer); + SAS_DPRINTK("executing SAS discovery task failed:%d\n", + res); + goto ex_err; + } + wait_for_completion(&task->completion); + res = -ETASK; + if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { + int res2; + SAS_DPRINTK("task aborted, flags:0x%x\n", + task->task_state_flags); + res2 = i->dft->lldd_abort_task(task); + SAS_DPRINTK("came back from abort task\n"); + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + if (res2 == TMF_RESP_FUNC_COMPLETE) + continue; /* Retry the task */ + else + goto ex_err; + } + } + if (task->task_status.stat == SAM_BUSY || + task->task_status.stat == SAM_TASK_SET_FULL || + task->task_status.stat == SAS_QUEUE_FULL) { + SAS_DPRINTK("task: q busy, sleeping...\n"); + schedule_timeout_interruptible(HZ); + } else if (task->task_status.stat == SAM_CHECK_COND) { + struct scsi_sense_hdr shdr; + + if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size, + &shdr)) { + SAS_DPRINTK("couldn't normalize sense\n"); + continue; + } + if ((shdr.sense_key == 6 && shdr.asc == 0x29) || + (shdr.sense_key == 2 && shdr.asc == 4 && + shdr.ascq == 1)) { + SAS_DPRINTK("device %016llx LUN: %016llx " + "powering up or not ready yet, " + "sleeping...\n", + SAS_ADDR(task->dev->sas_addr), + SAS_ADDR(task->ssp_task.LUN)); + + schedule_timeout_interruptible(5*HZ); + } else if (shdr.sense_key == 1) { + res = 0; + break; + } else if (shdr.sense_key == 5) { + break; + } else { + SAS_DPRINTK("dev %016llx LUN: %016llx " + "sense key:0x%x ASC:0x%x ASCQ:0x%x" + "\n", + SAS_ADDR(task->dev->sas_addr), + SAS_ADDR(task->ssp_task.LUN), + shdr.sense_key, + shdr.asc, shdr.ascq); + } + } else if (task->task_status.resp != SAS_TASK_COMPLETE || + task->task_status.stat != SAM_GOOD) { + SAS_DPRINTK("task finished with resp:0x%x, " + "stat:0x%x\n", + task->task_status.resp, + task->task_status.stat); + goto ex_err; + } else { + res = 0; + break; + } + } +ex_err: + if (pci_dma_dir != PCI_DMA_NONE) { + if (sas_protocol_ata(task->task_proto)) + pci_unmap_sg(task->dev->port->ha->pcidev, + task->scatter, task->num_scatter, + task->data_dir); + kfree(scatter); + } +out: + return res; +} + +/* ---------- SATA ---------- */ + +static void sas_get_ata_command_set(struct domain_device *dev) +{ + struct dev_to_host_fis *fis = + (struct dev_to_host_fis *) dev->frame_rcvd; + + if ((fis->sector_count == 1 && /* ATA */ + fis->lbal == 1 && + fis->lbam == 0 && + fis->lbah == 0 && + fis->device == 0) + || + (fis->sector_count == 0 && /* CE-ATA (mATA) */ + fis->lbal == 0 && + fis->lbam == 0xCE && + fis->lbah == 0xAA && + (fis->device & ~0x10) == 0)) + + dev->sata_dev.command_set = ATA_COMMAND_SET; + + else if ((fis->interrupt_reason == 1 && /* ATAPI */ + fis->lbal == 1 && + fis->byte_count_low == 0x14 && + fis->byte_count_high == 0xEB && + (fis->device & ~0x10) == 0)) + + dev->sata_dev.command_set = ATAPI_COMMAND_SET; + + else if ((fis->sector_count == 1 && /* SEMB */ + fis->lbal == 1 && + fis->lbam == 0x3C && + fis->lbah == 0xC3 && + fis->device == 0) + || + (fis->interrupt_reason == 1 && /* SATA PM */ + fis->lbal == 1 && + fis->byte_count_low == 0x69 && + fis->byte_count_high == 0x96 && + (fis->device & ~0x10) == 0)) + + /* Treat it as a superset? */ + dev->sata_dev.command_set = ATAPI_COMMAND_SET; +} + +/** + * sas_issue_ata_cmd -- Basic SATA command processing for discovery + * @dev: the device to send the command to + * @command: the command register + * @features: the features register + * @buffer: pointer to buffer to do I/O + * @size: size of @buffer + * @pci_dma_dir: PCI_DMA_... + */ +static int sas_issue_ata_cmd(struct domain_device *dev, u8 command, + u8 features, void *buffer, int size, + int pci_dma_dir) +{ + int res = 0; + struct sas_task *task; + struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *) + &dev->frame_rcvd[0]; + + res = -ENOMEM; + task = sas_alloc_task(GFP_KERNEL); + if (!task) + goto out; + + task->dev = dev; + + task->ata_task.fis.fis_type = 0x27; + task->ata_task.fis.command = command; + task->ata_task.fis.features = features; + task->ata_task.fis.device = d2h_fis->device; + task->ata_task.retry_count = 1; + + res = sas_execute_task(task, buffer, size, pci_dma_dir); + + sas_free_task(task); +out: + return res; +} + +static void sas_sata_propagate_sas_addr(struct domain_device *dev) +{ + unsigned long flags; + struct asd_sas_port *port = dev->port; + struct asd_sas_phy *phy; + + BUG_ON(dev->parent); + + memcpy(port->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); + spin_lock_irqsave(&port->phy_list_lock, flags); + list_for_each_entry(phy, &port->phy_list, port_phy_el) + memcpy(phy->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); + spin_unlock_irqrestore(&port->phy_list_lock, flags); +} + +#define ATA_IDENTIFY_DEV 0xEC +#define ATA_IDENTIFY_PACKET_DEV 0xA1 +#define ATA_SET_FEATURES 0xEF +#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07 + +/** + * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV) + * @dev: STP/SATA device of interest (ATA/ATAPI) + * + * The LLDD has already been notified of this device, so that we can + * send FISes to it. Here we try to get IDENTIFY DEVICE or IDENTIFY + * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its + * performance for this device. + */ +static int sas_discover_sata_dev(struct domain_device *dev) +{ + int res; + __le16 *identify_x; + u8 command; + + identify_x = kzalloc(512, GFP_KERNEL); + if (!identify_x) + return -ENOMEM; + + if (dev->sata_dev.command_set == ATA_COMMAND_SET) { + dev->sata_dev.identify_device = identify_x; + command = ATA_IDENTIFY_DEV; + } else { + dev->sata_dev.identify_packet_device = identify_x; + command = ATA_IDENTIFY_PACKET_DEV; + } + + res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, + PCI_DMA_FROMDEVICE); + if (res) + goto out_err; + + /* lives on the media? */ + if (le16_to_cpu(identify_x[0]) & 4) { + /* incomplete response */ + SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to " + "dev %llx\n", SAS_ADDR(dev->sas_addr)); + if (!le16_to_cpu(identify_x[83] & (1<<6))) + goto cont1; + res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES, + ATA_FEATURE_PUP_STBY_SPIN_UP, + NULL, 0, PCI_DMA_NONE); + if (res) + goto cont1; + + schedule_timeout_interruptible(5*HZ); /* More time? */ + res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, + PCI_DMA_FROMDEVICE); + if (res) + goto out_err; + } +cont1: + /* Get WWN */ + if (dev->port->oob_mode != SATA_OOB_MODE) { + memcpy(dev->sas_addr, dev->sata_dev.rps_resp.rps.stp_sas_addr, + SAS_ADDR_SIZE); + } else if (dev->sata_dev.command_set == ATA_COMMAND_SET && + (le16_to_cpu(dev->sata_dev.identify_device[108]) & 0xF000) + == 0x5000) { + int i; + + for (i = 0; i < 4; i++) { + dev->sas_addr[2*i] = + (le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0xFF00) >> 8; + dev->sas_addr[2*i+1] = + le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0x00FF; + } + } + sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr); + if (!dev->parent) + sas_sata_propagate_sas_addr(dev); + + /* XXX Hint: register this SATA device with SATL. + When this returns, dev->sata_dev->lu is alive and + present. + sas_satl_register_dev(dev); + */ + + sas_fill_in_rphy(dev, dev->rphy); + + return 0; +out_err: + dev->sata_dev.identify_packet_device = NULL; + dev->sata_dev.identify_device = NULL; + kfree(identify_x); + return res; +} + +static int sas_discover_sata_pm(struct domain_device *dev) +{ + return -ENODEV; +} + +/** + * sas_discover_sata -- discover an STP/SATA domain device + * @dev: pointer to struct domain_device of interest + * + * First we notify the LLDD of this device, so we can send frames to + * it. Then depending on the type of device we call the appropriate + * discover functions. Once device discover is done, we notify the + * LLDD so that it can fine-tune its parameters for the device, by + * removing it and then adding it. That is, the second time around, + * the driver would have certain fields, that it is looking at, set. + * Finally we initialize the kobj so that the device can be added to + * the system at registration time. Devices directly attached to a HA + * port, have no parents. All other devices do, and should have their + * "parent" pointer set appropriately before calling this function. + */ +int sas_discover_sata(struct domain_device *dev) +{ + int res; + + sas_get_ata_command_set(dev); + + res = sas_notify_lldd_dev_found(dev); + if (res) + return res; + + switch (dev->dev_type) { + case SATA_DEV: + res = sas_discover_sata_dev(dev); + break; + case SATA_PM: + res = sas_discover_sata_pm(dev); + break; + default: + break; + } + sas_notify_lldd_dev_gone(dev); + if (!res) { + sas_notify_lldd_dev_found(dev); + res = sas_rphy_add(dev->rphy); + } + + return res; +} diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index a65598b1e53..6ac9f61d006 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -55,149 +55,6 @@ void sas_init_dev(struct domain_device *dev) } } -static void sas_task_timedout(unsigned long _task) -{ - struct sas_task *task = (void *) _task; - unsigned long flags; - - spin_lock_irqsave(&task->task_state_lock, flags); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) - task->task_state_flags |= SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - complete(&task->completion); -} - -static void sas_disc_task_done(struct sas_task *task) -{ - if (!del_timer(&task->timer)) - return; - complete(&task->completion); -} - -#define SAS_DEV_TIMEOUT 10 - -/** - * sas_execute_task -- Basic task processing for discovery - * @task: the task to be executed - * @buffer: pointer to buffer to do I/O - * @size: size of @buffer - * @pci_dma_dir: PCI_DMA_... - */ -static int sas_execute_task(struct sas_task *task, void *buffer, int size, - int pci_dma_dir) -{ - int res = 0; - struct scatterlist *scatter = NULL; - struct task_status_struct *ts = &task->task_status; - int num_scatter = 0; - int retries = 0; - struct sas_internal *i = - to_sas_internal(task->dev->port->ha->core.shost->transportt); - - if (pci_dma_dir != PCI_DMA_NONE) { - scatter = kzalloc(sizeof(*scatter), GFP_KERNEL); - if (!scatter) - goto out; - - sg_init_one(scatter, buffer, size); - num_scatter = 1; - } - - task->task_proto = task->dev->tproto; - task->scatter = scatter; - task->num_scatter = num_scatter; - task->total_xfer_len = size; - task->data_dir = pci_dma_dir; - task->task_done = sas_disc_task_done; - - for (retries = 0; retries < 5; retries++) { - task->task_state_flags = SAS_TASK_STATE_PENDING; - init_completion(&task->completion); - - task->timer.data = (unsigned long) task; - task->timer.function = sas_task_timedout; - task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ; - add_timer(&task->timer); - - res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); - if (res) { - del_timer(&task->timer); - SAS_DPRINTK("executing SAS discovery task failed:%d\n", - res); - goto ex_err; - } - wait_for_completion(&task->completion); - res = -ETASK; - if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { - int res2; - SAS_DPRINTK("task aborted, flags:0x%x\n", - task->task_state_flags); - res2 = i->dft->lldd_abort_task(task); - SAS_DPRINTK("came back from abort task\n"); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { - if (res2 == TMF_RESP_FUNC_COMPLETE) - continue; /* Retry the task */ - else - goto ex_err; - } - } - if (task->task_status.stat == SAM_BUSY || - task->task_status.stat == SAM_TASK_SET_FULL || - task->task_status.stat == SAS_QUEUE_FULL) { - SAS_DPRINTK("task: q busy, sleeping...\n"); - schedule_timeout_interruptible(HZ); - } else if (task->task_status.stat == SAM_CHECK_COND) { - struct scsi_sense_hdr shdr; - - if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size, - &shdr)) { - SAS_DPRINTK("couldn't normalize sense\n"); - continue; - } - if ((shdr.sense_key == 6 && shdr.asc == 0x29) || - (shdr.sense_key == 2 && shdr.asc == 4 && - shdr.ascq == 1)) { - SAS_DPRINTK("device %016llx LUN: %016llx " - "powering up or not ready yet, " - "sleeping...\n", - SAS_ADDR(task->dev->sas_addr), - SAS_ADDR(task->ssp_task.LUN)); - - schedule_timeout_interruptible(5*HZ); - } else if (shdr.sense_key == 1) { - res = 0; - break; - } else if (shdr.sense_key == 5) { - break; - } else { - SAS_DPRINTK("dev %016llx LUN: %016llx " - "sense key:0x%x ASC:0x%x ASCQ:0x%x" - "\n", - SAS_ADDR(task->dev->sas_addr), - SAS_ADDR(task->ssp_task.LUN), - shdr.sense_key, - shdr.asc, shdr.ascq); - } - } else if (task->task_status.resp != SAS_TASK_COMPLETE || - task->task_status.stat != SAM_GOOD) { - SAS_DPRINTK("task finished with resp:0x%x, " - "stat:0x%x\n", - task->task_status.resp, - task->task_status.stat); - goto ex_err; - } else { - res = 0; - break; - } - } -ex_err: - if (pci_dma_dir != PCI_DMA_NONE) - kfree(scatter); -out: - return res; -} - /* ---------- Domain device discovery ---------- */ /** @@ -255,6 +112,7 @@ static int sas_get_port_device(struct asd_sas_port *port) switch (dev->dev_type) { case SAS_END_DEV: + case SATA_DEV: rphy = sas_end_device_alloc(port->port); break; case EDGE_DEV: @@ -265,7 +123,6 @@ static int sas_get_port_device(struct asd_sas_port *port) rphy = sas_expander_alloc(port->port, SAS_FANOUT_EXPANDER_DEVICE); break; - case SATA_DEV: default: printk("ERROR: Unidentified device type %d\n", dev->dev_type); rphy = NULL; @@ -292,207 +149,15 @@ static int sas_get_port_device(struct asd_sas_port *port) port->disc.max_level = 0; dev->rphy = rphy; - spin_lock(&port->dev_list_lock); + spin_lock_irq(&port->dev_list_lock); list_add_tail(&dev->dev_list_node, &port->dev_list); - spin_unlock(&port->dev_list_lock); + spin_unlock_irq(&port->dev_list_lock); return 0; } /* ---------- Discover and Revalidate ---------- */ -/* ---------- SATA ---------- */ - -static void sas_get_ata_command_set(struct domain_device *dev) -{ - struct dev_to_host_fis *fis = - (struct dev_to_host_fis *) dev->frame_rcvd; - - if ((fis->sector_count == 1 && /* ATA */ - fis->lbal == 1 && - fis->lbam == 0 && - fis->lbah == 0 && - fis->device == 0) - || - (fis->sector_count == 0 && /* CE-ATA (mATA) */ - fis->lbal == 0 && - fis->lbam == 0xCE && - fis->lbah == 0xAA && - (fis->device & ~0x10) == 0)) - - dev->sata_dev.command_set = ATA_COMMAND_SET; - - else if ((fis->interrupt_reason == 1 && /* ATAPI */ - fis->lbal == 1 && - fis->byte_count_low == 0x14 && - fis->byte_count_high == 0xEB && - (fis->device & ~0x10) == 0)) - - dev->sata_dev.command_set = ATAPI_COMMAND_SET; - - else if ((fis->sector_count == 1 && /* SEMB */ - fis->lbal == 1 && - fis->lbam == 0x3C && - fis->lbah == 0xC3 && - fis->device == 0) - || - (fis->interrupt_reason == 1 && /* SATA PM */ - fis->lbal == 1 && - fis->byte_count_low == 0x69 && - fis->byte_count_high == 0x96 && - (fis->device & ~0x10) == 0)) - - /* Treat it as a superset? */ - dev->sata_dev.command_set = ATAPI_COMMAND_SET; -} - -/** - * sas_issue_ata_cmd -- Basic SATA command processing for discovery - * @dev: the device to send the command to - * @command: the command register - * @features: the features register - * @buffer: pointer to buffer to do I/O - * @size: size of @buffer - * @pci_dma_dir: PCI_DMA_... - */ -static int sas_issue_ata_cmd(struct domain_device *dev, u8 command, - u8 features, void *buffer, int size, - int pci_dma_dir) -{ - int res = 0; - struct sas_task *task; - struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *) - &dev->frame_rcvd[0]; - - res = -ENOMEM; - task = sas_alloc_task(GFP_KERNEL); - if (!task) - goto out; - - task->dev = dev; - - task->ata_task.fis.command = command; - task->ata_task.fis.features = features; - task->ata_task.fis.device = d2h_fis->device; - task->ata_task.retry_count = 1; - - res = sas_execute_task(task, buffer, size, pci_dma_dir); - - sas_free_task(task); -out: - return res; -} - -static void sas_sata_propagate_sas_addr(struct domain_device *dev) -{ - unsigned long flags; - struct asd_sas_port *port = dev->port; - struct asd_sas_phy *phy; - - BUG_ON(dev->parent); - - memcpy(port->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); - spin_lock_irqsave(&port->phy_list_lock, flags); - list_for_each_entry(phy, &port->phy_list, port_phy_el) - memcpy(phy->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); - spin_unlock_irqrestore(&port->phy_list_lock, flags); -} - -#define ATA_IDENTIFY_DEV 0xEC -#define ATA_IDENTIFY_PACKET_DEV 0xA1 -#define ATA_SET_FEATURES 0xEF -#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07 - -/** - * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV) - * @dev: STP/SATA device of interest (ATA/ATAPI) - * - * The LLDD has already been notified of this device, so that we can - * send FISes to it. Here we try to get IDENTIFY DEVICE or IDENTIFY - * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its - * performance for this device. - */ -static int sas_discover_sata_dev(struct domain_device *dev) -{ - int res; - __le16 *identify_x; - u8 command; - - identify_x = kzalloc(512, GFP_KERNEL); - if (!identify_x) - return -ENOMEM; - - if (dev->sata_dev.command_set == ATA_COMMAND_SET) { - dev->sata_dev.identify_device = identify_x; - command = ATA_IDENTIFY_DEV; - } else { - dev->sata_dev.identify_packet_device = identify_x; - command = ATA_IDENTIFY_PACKET_DEV; - } - - res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, - PCI_DMA_FROMDEVICE); - if (res) - goto out_err; - - /* lives on the media? */ - if (le16_to_cpu(identify_x[0]) & 4) { - /* incomplete response */ - SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to " - "dev %llx\n", SAS_ADDR(dev->sas_addr)); - if (!le16_to_cpu(identify_x[83] & (1<<6))) - goto cont1; - res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES, - ATA_FEATURE_PUP_STBY_SPIN_UP, - NULL, 0, PCI_DMA_NONE); - if (res) - goto cont1; - - schedule_timeout_interruptible(5*HZ); /* More time? */ - res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, - PCI_DMA_FROMDEVICE); - if (res) - goto out_err; - } -cont1: - /* Get WWN */ - if (dev->port->oob_mode != SATA_OOB_MODE) { - memcpy(dev->sas_addr, dev->sata_dev.rps_resp.rps.stp_sas_addr, - SAS_ADDR_SIZE); - } else if (dev->sata_dev.command_set == ATA_COMMAND_SET && - (le16_to_cpu(dev->sata_dev.identify_device[108]) & 0xF000) - == 0x5000) { - int i; - - for (i = 0; i < 4; i++) { - dev->sas_addr[2*i] = - (le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0xFF00) >> 8; - dev->sas_addr[2*i+1] = - le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0x00FF; - } - } - sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr); - if (!dev->parent) - sas_sata_propagate_sas_addr(dev); - - /* XXX Hint: register this SATA device with SATL. - When this returns, dev->sata_dev->lu is alive and - present. - sas_satl_register_dev(dev); - */ - return 0; -out_err: - dev->sata_dev.identify_packet_device = NULL; - dev->sata_dev.identify_device = NULL; - kfree(identify_x); - return res; -} - -static int sas_discover_sata_pm(struct domain_device *dev) -{ - return -ENODEV; -} - int sas_notify_lldd_dev_found(struct domain_device *dev) { int res = 0; @@ -525,60 +190,6 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) /* ---------- Common/dispatchers ---------- */ -/** - * sas_discover_sata -- discover an STP/SATA domain device - * @dev: pointer to struct domain_device of interest - * - * First we notify the LLDD of this device, so we can send frames to - * it. Then depending on the type of device we call the appropriate - * discover functions. Once device discover is done, we notify the - * LLDD so that it can fine-tune its parameters for the device, by - * removing it and then adding it. That is, the second time around, - * the driver would have certain fields, that it is looking at, set. - * Finally we initialize the kobj so that the device can be added to - * the system at registration time. Devices directly attached to a HA - * port, have no parents. All other devices do, and should have their - * "parent" pointer set appropriately before calling this function. - */ -int sas_discover_sata(struct domain_device *dev) -{ - int res; - - sas_get_ata_command_set(dev); - - res = sas_notify_lldd_dev_found(dev); - if (res) - goto out_err2; - - switch (dev->dev_type) { - case SATA_DEV: - res = sas_discover_sata_dev(dev); - break; - case SATA_PM: - res = sas_discover_sata_pm(dev); - break; - default: - break; - } - if (res) - goto out_err; - - sas_notify_lldd_dev_gone(dev); - res = sas_notify_lldd_dev_found(dev); - if (res) - goto out_err2; - - res = sas_rphy_add(dev->rphy); - if (res) - goto out_err; - - return res; - -out_err: - sas_notify_lldd_dev_gone(dev); -out_err2: - return res; -} /** * sas_discover_end_dev -- discover an end device (SSP, etc) @@ -685,11 +296,14 @@ static void sas_discover_domain(struct work_struct *work) case FANOUT_DEV: error = sas_discover_root_expander(dev); break; +#ifdef CONFIG_SCSI_SAS_ATA case SATA_DEV: case SATA_PM: error = sas_discover_sata(dev); break; +#endif default: + error = -ENXIO; SAS_DPRINTK("unhandled device %d\n", dev->dev_type); break; } @@ -698,9 +312,9 @@ static void sas_discover_domain(struct work_struct *work) sas_rphy_free(dev->rphy); dev->rphy = NULL; - spin_lock(&port->dev_list_lock); + spin_lock_irq(&port->dev_list_lock); list_del_init(&dev->dev_list_node); - spin_unlock(&port->dev_list_lock); + spin_unlock_irq(&port->dev_list_lock); kfree(dev); /* not kobject_register-ed yet */ port->port_dev = NULL; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 23e90c5f8f3..b500f0c1449 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -23,6 +23,7 @@ */ #include <linux/scatterlist.h> +#include <linux/blkdev.h> #include "sas_internal.h" @@ -36,14 +37,6 @@ static int sas_configure_phy(struct domain_device *dev, int phy_id, u8 *sas_addr, int include); static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr); -#if 0 -/* FIXME: smp needs to migrate into the sas class */ -static ssize_t smp_portal_read(struct kobject *, struct bin_attribute *, - char *, loff_t, size_t); -static ssize_t smp_portal_write(struct kobject *, struct bin_attribute *, - char *, loff_t, size_t); -#endif - /* ---------- SMP task management ---------- */ static void smp_task_timedout(unsigned long _task) @@ -220,6 +213,36 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, #define DISCOVER_REQ_SIZE 16 #define DISCOVER_RESP_SIZE 56 +static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, + u8 *disc_resp, int single) +{ + int i, res; + + disc_req[9] = single; + for (i = 1 ; i < 3; i++) { + struct discover_resp *dr; + + res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, + disc_resp, DISCOVER_RESP_SIZE); + if (res) + return res; + /* This is detecting a failure to transmit inital + * dev to host FIS as described in section G.5 of + * sas-2 r 04b */ + dr = &((struct smp_resp *)disc_resp)->disc; + if (!(dr->attached_dev_type == 0 && + dr->attached_sata_dev)) + break; + /* In order to generate the dev to host FIS, we + * send a link reset to the expander port */ + sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL); + /* Wait for the reset to trigger the negotiation */ + msleep(500); + } + sas_set_ex_phy(dev, single, disc_resp); + return 0; +} + static int sas_ex_phy_discover(struct domain_device *dev, int single) { struct expander_device *ex = &dev->ex_dev; @@ -240,23 +263,15 @@ static int sas_ex_phy_discover(struct domain_device *dev, int single) disc_req[1] = SMP_DISCOVER; if (0 <= single && single < ex->num_phys) { - disc_req[9] = single; - res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, - disc_resp, DISCOVER_RESP_SIZE); - if (res) - goto out_err; - sas_set_ex_phy(dev, single, disc_resp); + res = sas_ex_phy_discover_helper(dev, disc_req, disc_resp, single); } else { int i; for (i = 0; i < ex->num_phys; i++) { - disc_req[9] = i; - res = smp_execute_task(dev, disc_req, - DISCOVER_REQ_SIZE, disc_resp, - DISCOVER_RESP_SIZE); + res = sas_ex_phy_discover_helper(dev, disc_req, + disc_resp, i); if (res) goto out_err; - sas_set_ex_phy(dev, i, disc_resp); } } out_err: @@ -520,6 +535,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy) } +#ifdef CONFIG_SCSI_SAS_ATA + #define RPS_REQ_SIZE 16 #define RPS_RESP_SIZE 60 @@ -529,6 +546,7 @@ static int sas_get_report_phy_sata(struct domain_device *dev, { int res; u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); + u8 *resp = (u8 *)rps_resp; if (!rps_req) return -ENOMEM; @@ -539,9 +557,30 @@ static int sas_get_report_phy_sata(struct domain_device *dev, res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE, rps_resp, RPS_RESP_SIZE); + /* 0x34 is the FIS type for the D2H fis. There's a potential + * standards cockup here. sas-2 explicitly specifies the FIS + * should be encoded so that FIS type is in resp[24]. + * However, some expanders endian reverse this. Undo the + * reversal here */ + if (!res && resp[27] == 0x34 && resp[24] != 0x34) { + int i; + + for (i = 0; i < 5; i++) { + int j = 24 + (i*4); + u8 a, b; + a = resp[j + 0]; + b = resp[j + 1]; + resp[j + 0] = resp[j + 3]; + resp[j + 1] = resp[j + 2]; + resp[j + 2] = b; + resp[j + 3] = a; + } + } + kfree(rps_req); - return 0; + return res; } +#endif static void sas_ex_get_linkrate(struct domain_device *parent, struct domain_device *child, @@ -609,6 +648,7 @@ static struct domain_device *sas_ex_discover_end_dev( } sas_ex_get_linkrate(parent, child, phy); +#ifdef CONFIG_SCSI_SAS_ATA if ((phy->attached_tproto & SAS_PROTO_STP) || phy->attached_sata_dev) { child->dev_type = SATA_DEV; if (phy->attached_tproto & SAS_PROTO_STP) @@ -625,16 +665,30 @@ static struct domain_device *sas_ex_discover_end_dev( } memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, sizeof(struct dev_to_host_fis)); + + rphy = sas_end_device_alloc(phy->port); + if (unlikely(!rphy)) + goto out_free; + sas_init_dev(child); + + child->rphy = rphy; + + spin_lock_irq(&parent->port->dev_list_lock); + list_add_tail(&child->dev_list_node, &parent->port->dev_list); + spin_unlock_irq(&parent->port->dev_list_lock); + res = sas_discover_sata(child); if (res) { SAS_DPRINTK("sas_discover_sata() for device %16llx at " "%016llx:0x%x returned 0x%x\n", SAS_ADDR(child->sas_addr), SAS_ADDR(parent->sas_addr), phy_id, res); - goto out_free; + goto out_list_del; } - } else if (phy->attached_tproto & SAS_PROTO_SSP) { + } else +#endif + if (phy->attached_tproto & SAS_PROTO_SSP) { child->dev_type = SAS_END_DEV; rphy = sas_end_device_alloc(phy->port); /* FIXME: error handling */ @@ -646,9 +700,9 @@ static struct domain_device *sas_ex_discover_end_dev( child->rphy = rphy; sas_fill_in_rphy(child, rphy); - spin_lock(&parent->port->dev_list_lock); + spin_lock_irq(&parent->port->dev_list_lock); list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock(&parent->port->dev_list_lock); + spin_unlock_irq(&parent->port->dev_list_lock); res = sas_discover_end_dev(child); if (res) { @@ -662,6 +716,7 @@ static struct domain_device *sas_ex_discover_end_dev( SAS_DPRINTK("target proto 0x%x at %016llx:0x%x not handled\n", phy->attached_tproto, SAS_ADDR(parent->sas_addr), phy_id); + goto out_free; } list_add_tail(&child->siblings, &parent_ex->children); @@ -761,9 +816,9 @@ static struct domain_device *sas_ex_discover_expander( sas_fill_in_rphy(child, rphy); sas_rphy_add(rphy); - spin_lock(&parent->port->dev_list_lock); + spin_lock_irq(&parent->port->dev_list_lock); list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock(&parent->port->dev_list_lock); + spin_unlock_irq(&parent->port->dev_list_lock); res = sas_discover_expander(child); if (res) { @@ -1359,30 +1414,6 @@ static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr) return 0; } -#if 0 -#define SMP_BIN_ATTR_NAME "smp_portal" - -static void sas_ex_smp_hook(struct domain_device *dev) -{ - struct expander_device *ex_dev = &dev->ex_dev; - struct bin_attribute *bin_attr = &ex_dev->smp_bin_attr; - - memset(bin_attr, 0, sizeof(*bin_attr)); - - bin_attr->attr.name = SMP_BIN_ATTR_NAME; - bin_attr->attr.mode = 0600; - - bin_attr->size = 0; - bin_attr->private = NULL; - bin_attr->read = smp_portal_read; - bin_attr->write= smp_portal_write; - bin_attr->mmap = NULL; - - ex_dev->smp_portal_pid = -1; - init_MUTEX(&ex_dev->smp_sema); -} -#endif - /** * sas_discover_expander -- expander discovery * @ex: pointer to expander domain device @@ -1844,76 +1875,49 @@ out: return res; } -#if 0 -/* ---------- SMP portal ---------- */ - -static ssize_t smp_portal_write(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t offs, size_t size) +int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, + struct request *req) { - struct domain_device *dev = to_dom_device(kobj); - struct expander_device *ex = &dev->ex_dev; - - if (offs != 0) - return -EFBIG; - else if (size == 0) - return 0; + struct domain_device *dev; + int ret, type = rphy->identify.device_type; + struct request *rsp = req->next_rq; - down_interruptible(&ex->smp_sema); - if (ex->smp_req) - kfree(ex->smp_req); - ex->smp_req = kzalloc(size, GFP_USER); - if (!ex->smp_req) { - up(&ex->smp_sema); - return -ENOMEM; + if (!rsp) { + printk("%s: space for a smp response is missing\n", + __FUNCTION__); + return -EINVAL; } - memcpy(ex->smp_req, buf, size); - ex->smp_req_size = size; - ex->smp_portal_pid = current->pid; - up(&ex->smp_sema); - return size; -} - -static ssize_t smp_portal_read(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t offs, size_t size) -{ - struct domain_device *dev = to_dom_device(kobj); - struct expander_device *ex = &dev->ex_dev; - u8 *smp_resp; - int res = -EINVAL; - - /* XXX: sysfs gives us an offset of 0x10 or 0x8 while in fact - * it should be 0. - */ + /* seems aic94xx doesn't support */ + if (!rphy) { + printk("%s: can we send a smp request to a host?\n", + __FUNCTION__); + return -EINVAL; + } - down_interruptible(&ex->smp_sema); - if (!ex->smp_req || ex->smp_portal_pid != current->pid) - goto out; + if (type != SAS_EDGE_EXPANDER_DEVICE && + type != SAS_FANOUT_EXPANDER_DEVICE) { + printk("%s: can we send a smp request to a device?\n", + __FUNCTION__); + return -EINVAL; + } - res = 0; - if (size == 0) - goto out; + dev = sas_find_dev_by_rphy(rphy); + if (!dev) { + printk("%s: fail to find a domain_device?\n", __FUNCTION__); + return -EINVAL; + } - res = -ENOMEM; - smp_resp = alloc_smp_resp(size); - if (!smp_resp) - goto out; - res = smp_execute_task(dev, ex->smp_req, ex->smp_req_size, - smp_resp, size); - if (!res) { - memcpy(buf, smp_resp, size); - res = size; + /* do we need to support multiple segments? */ + if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) { + printk("%s: multiple segments req %u %u, rsp %u %u\n", + __FUNCTION__, req->bio->bi_vcnt, req->data_len, + rsp->bio->bi_vcnt, rsp->data_len); + return -EINVAL; } - kfree(smp_resp); -out: - kfree(ex->smp_req); - ex->smp_req = NULL; - ex->smp_req_size = 0; - ex->smp_portal_pid = -1; - up(&ex->smp_sema); - return res; + ret = smp_execute_task(dev, bio_data(req->bio), req->data_len, + bio_data(rsp->bio), rsp->data_len); + + return ret; } -#endif diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 1396c83b0c9..9cd5abe9e71 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -259,6 +259,7 @@ static struct sas_function_template sft = { .phy_reset = sas_phy_reset, .set_phy_speed = sas_set_phy_speed, .get_linkerrors = sas_get_linkerrors, + .smp_handler = sas_smp_handler, }; struct scsi_transport_template * diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index a78638df201..2b8213b1832 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -39,6 +39,9 @@ #define SAS_DPRINTK(fmt, ...) #endif +#define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) +#define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) + void sas_scsi_recover_host(struct Scsi_Host *shost); int sas_show_class(enum sas_class class, char *buf); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 9c5342e7a69..7663841eb4c 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -34,6 +34,7 @@ #include <scsi/scsi_eh.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_sas.h> +#include <scsi/sas_ata.h> #include "../scsi_sas_internal.h" #include "../scsi_transport_api.h" #include "../scsi_priv.h" @@ -42,12 +43,10 @@ #include <linux/blkdev.h> #include <linux/freezer.h> #include <linux/scatterlist.h> +#include <linux/libata.h> /* ---------- SCSI Host glue ---------- */ -#define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) -#define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) - static void sas_scsi_task_done(struct sas_task *task) { struct task_status_struct *ts = &task->task_status; @@ -172,7 +171,7 @@ static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, return task; } -static int sas_queue_up(struct sas_task *task) +int sas_queue_up(struct sas_task *task) { struct sas_ha_struct *sas_ha = task->dev->port->ha; struct scsi_core *core = &sas_ha->core; @@ -213,6 +212,16 @@ int sas_queuecommand(struct scsi_cmnd *cmd, struct sas_ha_struct *sas_ha = dev->port->ha; struct sas_task *task; + if (dev_is_sata(dev)) { + unsigned long flags; + + spin_lock_irqsave(dev->sata_dev.ap->lock, flags); + res = ata_sas_queuecmd(cmd, scsi_done, + dev->sata_dev.ap); + spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); + goto out; + } + res = -ENOMEM; task = sas_create_task(cmd, dev, GFP_ATOMIC); if (!task) @@ -684,6 +693,16 @@ enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) return EH_NOT_HANDLED; } +int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +{ + struct domain_device *dev = sdev_to_domain_dev(sdev); + + if (dev_is_sata(dev)) + return ata_scsi_ioctl(sdev, cmd, arg); + + return -EINVAL; +} + struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy) { struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent); @@ -723,10 +742,17 @@ static inline struct domain_device *sas_find_target(struct scsi_target *starget) int sas_target_alloc(struct scsi_target *starget) { struct domain_device *found_dev = sas_find_target(starget); + int res; if (!found_dev) return -ENODEV; + if (dev_is_sata(found_dev)) { + res = sas_ata_init_host_and_port(found_dev, starget); + if (res) + return res; + } + starget->hostdata = found_dev; return 0; } @@ -741,6 +767,11 @@ int sas_slave_configure(struct scsi_device *scsi_dev) BUG_ON(dev->rphy->identify.device_type != SAS_END_DEVICE); + if (dev_is_sata(dev)) { + ata_sas_slave_configure(scsi_dev, dev->sata_dev.ap); + return 0; + } + sas_ha = dev->port->ha; sas_read_port_mode_page(scsi_dev); @@ -764,6 +795,10 @@ int sas_slave_configure(struct scsi_device *scsi_dev) void sas_slave_destroy(struct scsi_device *scsi_dev) { + struct domain_device *dev = sdev_to_domain_dev(scsi_dev); + + if (dev_is_sata(dev)) + ata_port_disable(dev->sata_dev.ap); } int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth) @@ -980,10 +1015,38 @@ void sas_task_abort(struct sas_task *task) return; } + if (dev_is_sata(task->dev)) { + sas_ata_task_abort(task); + return; + } + scsi_req_abort_cmd(sc); scsi_schedule_eh(sc->device->host); } +int sas_slave_alloc(struct scsi_device *scsi_dev) +{ + struct domain_device *dev = sdev_to_domain_dev(scsi_dev); + + if (dev_is_sata(dev)) + return ata_sas_port_init(dev->sata_dev.ap); + + return 0; +} + +void sas_target_destroy(struct scsi_target *starget) +{ + struct domain_device *found_dev = sas_find_target(starget); + + if (!found_dev) + return; + + if (dev_is_sata(found_dev)) + ata_sas_port_destroy(found_dev->sata_dev.ap); + + return; +} + EXPORT_SYMBOL_GPL(sas_queuecommand); EXPORT_SYMBOL_GPL(sas_target_alloc); EXPORT_SYMBOL_GPL(sas_slave_configure); @@ -997,3 +1060,6 @@ EXPORT_SYMBOL_GPL(sas_phy_reset); EXPORT_SYMBOL_GPL(sas_phy_enable); EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler); EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler); +EXPORT_SYMBOL_GPL(sas_slave_alloc); +EXPORT_SYMBOL_GPL(sas_target_destroy); +EXPORT_SYMBOL_GPL(sas_ioctl); diff --git a/drivers/scsi/mvme16x_scsi.c b/drivers/scsi/mvme16x_scsi.c index d6ef22a941c..1bdddad4857 100644 --- a/drivers/scsi/mvme16x_scsi.c +++ b/drivers/scsi/mvme16x_scsi.c @@ -89,6 +89,7 @@ mvme16x_probe(struct device *dev) out_be32(0xfff4202c, v); } + dev_set_drvdata(dev, host); scsi_scan_host(host); return 0; @@ -104,7 +105,7 @@ mvme16x_probe(struct device *dev) static __devexit int mvme16x_device_remove(struct device *dev) { - struct Scsi_Host *host = dev_to_shost(dev); + struct Scsi_Host *host = dev_get_drvdata(dev); struct NCR_700_Host_Parameters *hostdata = shost_priv(host); /* Disable scsi chip ints */ diff --git a/drivers/scsi/pcmcia/Kconfig b/drivers/scsi/pcmcia/Kconfig index 7dd787f6ab2..fa481b515ea 100644 --- a/drivers/scsi/pcmcia/Kconfig +++ b/drivers/scsi/pcmcia/Kconfig @@ -2,9 +2,12 @@ # PCMCIA SCSI adapter configuration # -menu "PCMCIA SCSI adapter support" +menuconfig SCSI_LOWLEVEL_PCMCIA + bool "PCMCIA SCSI adapter support" depends on SCSI!=n && PCMCIA!=n +if SCSI_LOWLEVEL_PCMCIA && SCSI && PCMCIA + config PCMCIA_AHA152X tristate "Adaptec AHA152X PCMCIA support" depends on !64BIT @@ -77,4 +80,4 @@ config PCMCIA_SYM53C500 To compile this driver as a module, choose M here: the module will be called sym53c500_cs. -endmenu +endif # SCSI_LOWLEVEL_PCMCIA diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 3eb2208675a..1612f9200a5 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -98,7 +98,7 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj, /* Read NVRAM. */ spin_lock_irqsave(&ha->hardware_lock, flags); - ha->isp_ops.read_nvram(ha, (uint8_t *)buf, ha->nvram_base, + ha->isp_ops->read_nvram(ha, (uint8_t *)buf, ha->nvram_base, ha->nvram_size); spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -119,7 +119,7 @@ qla2x00_sysfs_write_nvram(struct kobject *kobj, return 0; /* Checksum NVRAM. */ - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { uint32_t *iter; uint32_t chksum; @@ -143,7 +143,7 @@ qla2x00_sysfs_write_nvram(struct kobject *kobj, /* Write NVRAM. */ spin_lock_irqsave(&ha->hardware_lock, flags); - ha->isp_ops.write_nvram(ha, (uint8_t *)buf, ha->nvram_base, count); + ha->isp_ops->write_nvram(ha, (uint8_t *)buf, ha->nvram_base, count); spin_unlock_irqrestore(&ha->hardware_lock, flags); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); @@ -206,7 +206,7 @@ static struct bin_attribute sysfs_optrom_attr = { .name = "optrom", .mode = S_IRUSR | S_IWUSR, }, - .size = OPTROM_SIZE_24XX, + .size = 0, .read = qla2x00_sysfs_read_optrom, .write = qla2x00_sysfs_write_optrom, }; @@ -252,7 +252,7 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, } memset(ha->optrom_buffer, 0, ha->optrom_size); - ha->isp_ops.read_optrom(ha, ha->optrom_buffer, 0, + ha->isp_ops->read_optrom(ha, ha->optrom_buffer, 0, ha->optrom_size); break; case 2: @@ -275,7 +275,7 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, if (ha->optrom_state != QLA_SWRITING) break; - ha->isp_ops.write_optrom(ha, ha->optrom_buffer, 0, + ha->isp_ops->write_optrom(ha, ha->optrom_buffer, 0, ha->optrom_size); break; } @@ -305,7 +305,8 @@ qla2x00_sysfs_read_vpd(struct kobject *kobj, /* Read NVRAM. */ spin_lock_irqsave(&ha->hardware_lock, flags); - ha->isp_ops.read_nvram(ha, (uint8_t *)buf, ha->vpd_base, ha->vpd_size); + ha->isp_ops->read_nvram(ha, (uint8_t *)buf, ha->vpd_base, + ha->vpd_size); spin_unlock_irqrestore(&ha->hardware_lock, flags); return ha->vpd_size; @@ -325,7 +326,7 @@ qla2x00_sysfs_write_vpd(struct kobject *kobj, /* Write NVRAM. */ spin_lock_irqsave(&ha->hardware_lock, flags); - ha->isp_ops.write_nvram(ha, (uint8_t *)buf, ha->vpd_base, count); + ha->isp_ops->write_nvram(ha, (uint8_t *)buf, ha->vpd_base, count); spin_unlock_irqrestore(&ha->hardware_lock, flags); return count; @@ -410,7 +411,7 @@ qla2x00_alloc_sysfs_attr(scsi_qla_host_t *ha) int ret; for (iter = bin_file_entries; iter->name; iter++) { - if (iter->is4GBp_only && (!IS_QLA24XX(ha) && !IS_QLA54XX(ha))) + if (iter->is4GBp_only && !IS_FWI2_CAPABLE(ha)) continue; ret = sysfs_create_bin_file(&host->shost_gendev.kobj, @@ -429,7 +430,7 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *ha) struct sysfs_entry *iter; for (iter = bin_file_entries; iter->name; iter++) { - if (iter->is4GBp_only && (!IS_QLA24XX(ha) && !IS_QLA54XX(ha))) + if (iter->is4GBp_only && !IS_FWI2_CAPABLE(ha)) continue; sysfs_remove_bin_file(&host->shost_gendev.kobj, @@ -437,7 +438,7 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *ha) } if (ha->beacon_blink_led == 1) - ha->isp_ops.beacon_off(ha); + ha->isp_ops->beacon_off(ha); } /* Scsi_Host attributes. */ @@ -455,7 +456,7 @@ qla2x00_fw_version_show(struct class_device *cdev, char *buf) char fw_str[30]; return snprintf(buf, PAGE_SIZE, "%s\n", - ha->isp_ops.fw_version_str(ha, fw_str)); + ha->isp_ops->fw_version_str(ha, fw_str)); } static ssize_t @@ -507,7 +508,7 @@ qla2x00_pci_info_show(struct class_device *cdev, char *buf) char pci_info[30]; return snprintf(buf, PAGE_SIZE, "%s\n", - ha->isp_ops.pci_info_str(ha, pci_info)); + ha->isp_ops->pci_info_str(ha, pci_info)); } static ssize_t @@ -652,9 +653,9 @@ qla2x00_beacon_store(struct class_device *cdev, const char *buf, return -EINVAL; if (val) - rval = ha->isp_ops.beacon_on(ha); + rval = ha->isp_ops->beacon_on(ha); else - rval = ha->isp_ops.beacon_off(ha); + rval = ha->isp_ops->beacon_off(ha); if (rval != QLA_SUCCESS) count = 0; @@ -898,7 +899,7 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) pfc_host_stat = &ha->fc_host_stat; memset(pfc_host_stat, -1, sizeof(struct fc_host_statistics)); - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { rval = qla24xx_get_isp_stats(ha, (uint32_t *)&stat_buf, sizeof(stat_buf) / 4, mb_stat); } else if (atomic_read(&ha->loop_state) == LOOP_READY && diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 996c47a6307..563d18f4ff5 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -37,6 +37,121 @@ qla2xxx_copy_queues(scsi_qla_host_t *ha, void *ptr) return ptr + (ha->response_q_length * sizeof(response_t)); } +static int +qla2xxx_dump_memory(scsi_qla_host_t *ha, uint32_t *code_ram, + uint32_t cram_size, uint32_t *ext_mem, void **nxt) +{ + int rval; + uint32_t cnt, stat, timer, risc_address, ext_mem_cnt; + uint16_t mb[4]; + struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + + rval = QLA_SUCCESS; + risc_address = ext_mem_cnt = 0; + memset(mb, 0, sizeof(mb)); + + /* Code RAM. */ + risc_address = 0x20000; + WRT_REG_WORD(®->mailbox0, MBC_READ_RAM_EXTENDED); + clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); + + for (cnt = 0; cnt < cram_size / 4 && rval == QLA_SUCCESS; + cnt++, risc_address++) { + WRT_REG_WORD(®->mailbox1, LSW(risc_address)); + WRT_REG_WORD(®->mailbox8, MSW(risc_address)); + RD_REG_WORD(®->mailbox8); + WRT_REG_DWORD(®->hccr, HCCRX_SET_HOST_INT); + + for (timer = 6000000; timer; timer--) { + /* Check for pending interrupts. */ + stat = RD_REG_DWORD(®->host_status); + if (stat & HSRX_RISC_INT) { + stat &= 0xff; + + if (stat == 0x1 || stat == 0x2 || + stat == 0x10 || stat == 0x11) { + set_bit(MBX_INTERRUPT, + &ha->mbx_cmd_flags); + + mb[0] = RD_REG_WORD(®->mailbox0); + mb[2] = RD_REG_WORD(®->mailbox2); + mb[3] = RD_REG_WORD(®->mailbox3); + + WRT_REG_DWORD(®->hccr, + HCCRX_CLR_RISC_INT); + RD_REG_DWORD(®->hccr); + break; + } + + /* Clear this intr; it wasn't a mailbox intr */ + WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); + RD_REG_DWORD(®->hccr); + } + udelay(5); + } + + if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { + rval = mb[0] & MBS_MASK; + code_ram[cnt] = htonl((mb[3] << 16) | mb[2]); + } else { + rval = QLA_FUNCTION_FAILED; + } + } + + if (rval == QLA_SUCCESS) { + /* External Memory. */ + risc_address = 0x100000; + ext_mem_cnt = ha->fw_memory_size - 0x100000 + 1; + WRT_REG_WORD(®->mailbox0, MBC_READ_RAM_EXTENDED); + clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); + } + for (cnt = 0; cnt < ext_mem_cnt && rval == QLA_SUCCESS; + cnt++, risc_address++) { + WRT_REG_WORD(®->mailbox1, LSW(risc_address)); + WRT_REG_WORD(®->mailbox8, MSW(risc_address)); + RD_REG_WORD(®->mailbox8); + WRT_REG_DWORD(®->hccr, HCCRX_SET_HOST_INT); + + for (timer = 6000000; timer; timer--) { + /* Check for pending interrupts. */ + stat = RD_REG_DWORD(®->host_status); + if (stat & HSRX_RISC_INT) { + stat &= 0xff; + + if (stat == 0x1 || stat == 0x2 || + stat == 0x10 || stat == 0x11) { + set_bit(MBX_INTERRUPT, + &ha->mbx_cmd_flags); + + mb[0] = RD_REG_WORD(®->mailbox0); + mb[2] = RD_REG_WORD(®->mailbox2); + mb[3] = RD_REG_WORD(®->mailbox3); + + WRT_REG_DWORD(®->hccr, + HCCRX_CLR_RISC_INT); + RD_REG_DWORD(®->hccr); + break; + } + + /* Clear this intr; it wasn't a mailbox intr */ + WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); + RD_REG_DWORD(®->hccr); + } + udelay(5); + } + + if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { + rval = mb[0] & MBS_MASK; + ext_mem[cnt] = htonl((mb[3] << 16) | mb[2]); + } else { + rval = QLA_FUNCTION_FAILED; + } + } + + *nxt = rval == QLA_SUCCESS ? &ext_mem[cnt]: NULL; + return rval; +} + /** * qla2300_fw_dump() - Dumps binary data from the 2300 firmware. * @ha: HA context @@ -633,11 +748,10 @@ void qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) { int rval; - uint32_t cnt, timer; + uint32_t cnt; uint32_t risc_address; - uint16_t mb[4], wd; + uint16_t mb0, wd; - uint32_t stat; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; uint32_t __iomem *dmp_reg; uint32_t *iter_reg; @@ -645,10 +759,9 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) unsigned long flags; struct qla24xx_fw_dump *fw; uint32_t ext_mem_cnt; - void *eft; + void *nxt; risc_address = ext_mem_cnt = 0; - memset(mb, 0, sizeof(mb)); flags = 0; if (!hardware_locked) @@ -701,250 +814,236 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) /* Shadow registers. */ WRT_REG_DWORD(®->iobase_addr, 0x0F70); RD_REG_DWORD(®->iobase_addr); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xF0); - WRT_REG_DWORD(dmp_reg, 0xB0000000); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xFC); - fw->shadow_reg[0] = htonl(RD_REG_DWORD(dmp_reg)); - - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xF0); - WRT_REG_DWORD(dmp_reg, 0xB0100000); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xFC); - fw->shadow_reg[1] = htonl(RD_REG_DWORD(dmp_reg)); - - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xF0); - WRT_REG_DWORD(dmp_reg, 0xB0200000); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xFC); - fw->shadow_reg[2] = htonl(RD_REG_DWORD(dmp_reg)); - - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xF0); - WRT_REG_DWORD(dmp_reg, 0xB0300000); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xFC); - fw->shadow_reg[3] = htonl(RD_REG_DWORD(dmp_reg)); - - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xF0); - WRT_REG_DWORD(dmp_reg, 0xB0400000); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xFC); - fw->shadow_reg[4] = htonl(RD_REG_DWORD(dmp_reg)); - - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xF0); - WRT_REG_DWORD(dmp_reg, 0xB0500000); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xFC); - fw->shadow_reg[5] = htonl(RD_REG_DWORD(dmp_reg)); - - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xF0); - WRT_REG_DWORD(dmp_reg, 0xB0600000); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xFC); - fw->shadow_reg[6] = htonl(RD_REG_DWORD(dmp_reg)); + WRT_REG_DWORD(®->iobase_select, 0xB0000000); + fw->shadow_reg[0] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0100000); + fw->shadow_reg[1] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0200000); + fw->shadow_reg[2] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0300000); + fw->shadow_reg[3] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0400000); + fw->shadow_reg[4] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0500000); + fw->shadow_reg[5] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0600000); + fw->shadow_reg[6] = htonl(RD_REG_DWORD(®->iobase_sdata)); /* Mailbox registers. */ - mbx_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80); + mbx_reg = ®->mailbox0; for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++) fw->mailbox_reg[cnt] = htons(RD_REG_WORD(mbx_reg++)); /* Transfer sequence registers. */ iter_reg = fw->xseq_gp_reg; WRT_REG_DWORD(®->iobase_addr, 0xBF00); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xBF10); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xBF20); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xBF30); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xBF40); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xBF50); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xBF60); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xBF70); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xBFE0); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < sizeof(fw->xseq_0_reg) / 4; cnt++) fw->xseq_0_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xBFF0); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < sizeof(fw->xseq_1_reg) / 4; cnt++) fw->xseq_1_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); /* Receive sequence registers. */ iter_reg = fw->rseq_gp_reg; WRT_REG_DWORD(®->iobase_addr, 0xFF00); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xFF10); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xFF20); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xFF30); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xFF40); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xFF50); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xFF60); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xFF70); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xFFD0); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < sizeof(fw->rseq_0_reg) / 4; cnt++) fw->rseq_0_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xFFE0); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < sizeof(fw->rseq_1_reg) / 4; cnt++) fw->rseq_1_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0xFFF0); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < sizeof(fw->rseq_2_reg) / 4; cnt++) fw->rseq_2_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); /* Command DMA registers. */ WRT_REG_DWORD(®->iobase_addr, 0x7100); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < sizeof(fw->cmd_dma_reg) / 4; cnt++) fw->cmd_dma_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); /* Queues. */ iter_reg = fw->req0_dma_reg; WRT_REG_DWORD(®->iobase_addr, 0x7200); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 8; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xE4); + dmp_reg = ®->iobase_q; for (cnt = 0; cnt < 7; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); iter_reg = fw->resp0_dma_reg; WRT_REG_DWORD(®->iobase_addr, 0x7300); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 8; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xE4); + dmp_reg = ®->iobase_q; for (cnt = 0; cnt < 7; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); iter_reg = fw->req1_dma_reg; WRT_REG_DWORD(®->iobase_addr, 0x7400); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 8; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xE4); + dmp_reg = ®->iobase_q; for (cnt = 0; cnt < 7; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); /* Transmit DMA registers. */ iter_reg = fw->xmt0_dma_reg; WRT_REG_DWORD(®->iobase_addr, 0x7600); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x7610); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); iter_reg = fw->xmt1_dma_reg; WRT_REG_DWORD(®->iobase_addr, 0x7620); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x7630); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); iter_reg = fw->xmt2_dma_reg; WRT_REG_DWORD(®->iobase_addr, 0x7640); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x7650); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); iter_reg = fw->xmt3_dma_reg; WRT_REG_DWORD(®->iobase_addr, 0x7660); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x7670); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); iter_reg = fw->xmt4_dma_reg; WRT_REG_DWORD(®->iobase_addr, 0x7680); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x7690); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x76A0); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < sizeof(fw->xmt_data_dma_reg) / 4; cnt++) fw->xmt_data_dma_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); @@ -952,221 +1051,221 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) /* Receive DMA registers. */ iter_reg = fw->rcvt0_data_dma_reg; WRT_REG_DWORD(®->iobase_addr, 0x7700); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x7710); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); iter_reg = fw->rcvt1_data_dma_reg; WRT_REG_DWORD(®->iobase_addr, 0x7720); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x7730); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); /* RISC registers. */ iter_reg = fw->risc_gp_reg; WRT_REG_DWORD(®->iobase_addr, 0x0F00); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x0F10); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x0F20); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x0F30); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x0F40); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x0F50); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x0F60); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x0F70); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); /* Local memory controller registers. */ iter_reg = fw->lmc_reg; WRT_REG_DWORD(®->iobase_addr, 0x3000); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x3010); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x3020); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x3030); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x3040); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x3050); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x3060); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); /* Fibre Protocol Module registers. */ iter_reg = fw->fpm_hdw_reg; WRT_REG_DWORD(®->iobase_addr, 0x4000); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x4010); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x4020); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x4030); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x4040); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x4050); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x4060); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x4070); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x4080); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x4090); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x40A0); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x40B0); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); /* Frame Buffer registers. */ iter_reg = fw->fb_hdw_reg; WRT_REG_DWORD(®->iobase_addr, 0x6000); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x6010); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x6020); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x6030); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x6040); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x6100); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x6130); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x6150); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x6170); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x6190); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); WRT_REG_DWORD(®->iobase_addr, 0x61B0); - dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xC0); + dmp_reg = ®->iobase_window; for (cnt = 0; cnt < 16; cnt++) *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); @@ -1187,10 +1286,10 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) udelay(100); /* Wait for firmware to complete NVRAM accesses. */ - mb[0] = (uint32_t) RD_REG_WORD(®->mailbox0); - for (cnt = 10000 ; cnt && mb[0]; cnt--) { + mb0 = (uint32_t) RD_REG_WORD(®->mailbox0); + for (cnt = 10000 ; cnt && mb0; cnt--) { udelay(5); - mb[0] = (uint32_t) RD_REG_WORD(®->mailbox0); + mb0 = (uint32_t) RD_REG_WORD(®->mailbox0); barrier(); } @@ -1214,110 +1313,717 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) rval = QLA_FUNCTION_TIMEOUT; } - /* Memory. */ + if (rval == QLA_SUCCESS) + rval = qla2xxx_dump_memory(ha, fw->code_ram, + sizeof(fw->code_ram), fw->ext_mem, &nxt); + if (rval == QLA_SUCCESS) { - /* Code RAM. */ - risc_address = 0x20000; - WRT_REG_WORD(®->mailbox0, MBC_READ_RAM_EXTENDED); - clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); + nxt = qla2xxx_copy_queues(ha, nxt); + if (ha->eft) + memcpy(nxt, ha->eft, ntohl(ha->fw_dump->eft_size)); } - for (cnt = 0; cnt < sizeof(fw->code_ram) / 4 && rval == QLA_SUCCESS; - cnt++, risc_address++) { - WRT_REG_WORD(®->mailbox1, LSW(risc_address)); - WRT_REG_WORD(®->mailbox8, MSW(risc_address)); - RD_REG_WORD(®->mailbox8); - WRT_REG_DWORD(®->hccr, HCCRX_SET_HOST_INT); - for (timer = 6000000; timer; timer--) { - /* Check for pending interrupts. */ - stat = RD_REG_DWORD(®->host_status); - if (stat & HSRX_RISC_INT) { - stat &= 0xff; + if (rval != QLA_SUCCESS) { + qla_printk(KERN_WARNING, ha, + "Failed to dump firmware (%x)!!!\n", rval); + ha->fw_dumped = 0; - if (stat == 0x1 || stat == 0x2 || - stat == 0x10 || stat == 0x11) { - set_bit(MBX_INTERRUPT, - &ha->mbx_cmd_flags); + } else { + qla_printk(KERN_INFO, ha, + "Firmware dump saved to temp buffer (%ld/%p).\n", + ha->host_no, ha->fw_dump); + ha->fw_dumped = 1; + } - mb[0] = RD_REG_WORD(®->mailbox0); - mb[2] = RD_REG_WORD(®->mailbox2); - mb[3] = RD_REG_WORD(®->mailbox3); +qla24xx_fw_dump_failed: + if (!hardware_locked) + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} - WRT_REG_DWORD(®->hccr, - HCCRX_CLR_RISC_INT); - RD_REG_DWORD(®->hccr); - break; - } +void +qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) +{ + int rval; + uint32_t cnt; + uint32_t risc_address; + uint16_t mb0, wd; - /* Clear this intr; it wasn't a mailbox intr */ - WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); - RD_REG_DWORD(®->hccr); - } - udelay(5); - } + struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + uint32_t __iomem *dmp_reg; + uint32_t *iter_reg; + uint16_t __iomem *mbx_reg; + unsigned long flags; + struct qla25xx_fw_dump *fw; + uint32_t ext_mem_cnt; + void *nxt; - if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { - rval = mb[0] & MBS_MASK; - fw->code_ram[cnt] = htonl((mb[3] << 16) | mb[2]); - } else { - rval = QLA_FUNCTION_FAILED; + risc_address = ext_mem_cnt = 0; + flags = 0; + + if (!hardware_locked) + spin_lock_irqsave(&ha->hardware_lock, flags); + + if (!ha->fw_dump) { + qla_printk(KERN_WARNING, ha, + "No buffer available for dump!!!\n"); + goto qla25xx_fw_dump_failed; + } + + if (ha->fw_dumped) { + qla_printk(KERN_WARNING, ha, + "Firmware has been previously dumped (%p) -- ignoring " + "request...\n", ha->fw_dump); + goto qla25xx_fw_dump_failed; + } + fw = &ha->fw_dump->isp.isp25; + qla2xxx_prep_dump(ha, ha->fw_dump); + + rval = QLA_SUCCESS; + fw->host_status = htonl(RD_REG_DWORD(®->host_status)); + + /* Pause RISC. */ + if ((RD_REG_DWORD(®->hccr) & HCCRX_RISC_PAUSE) == 0) { + WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_RESET | + HCCRX_CLR_HOST_INT); + RD_REG_DWORD(®->hccr); /* PCI Posting. */ + WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_PAUSE); + for (cnt = 30000; + (RD_REG_DWORD(®->hccr) & HCCRX_RISC_PAUSE) == 0 && + rval == QLA_SUCCESS; cnt--) { + if (cnt) + udelay(100); + else + rval = QLA_FUNCTION_TIMEOUT; } } if (rval == QLA_SUCCESS) { - /* External Memory. */ - risc_address = 0x100000; - ext_mem_cnt = ha->fw_memory_size - 0x100000 + 1; - WRT_REG_WORD(®->mailbox0, MBC_READ_RAM_EXTENDED); - clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); - } - for (cnt = 0; cnt < ext_mem_cnt && rval == QLA_SUCCESS; - cnt++, risc_address++) { - WRT_REG_WORD(®->mailbox1, LSW(risc_address)); - WRT_REG_WORD(®->mailbox8, MSW(risc_address)); - RD_REG_WORD(®->mailbox8); - WRT_REG_DWORD(®->hccr, HCCRX_SET_HOST_INT); + /* Host interface registers. */ + dmp_reg = (uint32_t __iomem *)(reg + 0); + for (cnt = 0; cnt < sizeof(fw->host_reg) / 4; cnt++) + fw->host_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); - for (timer = 6000000; timer; timer--) { - /* Check for pending interrupts. */ - stat = RD_REG_DWORD(®->host_status); - if (stat & HSRX_RISC_INT) { - stat &= 0xff; + /* Disable interrupts. */ + WRT_REG_DWORD(®->ictrl, 0); + RD_REG_DWORD(®->ictrl); - if (stat == 0x1 || stat == 0x2 || - stat == 0x10 || stat == 0x11) { - set_bit(MBX_INTERRUPT, - &ha->mbx_cmd_flags); + /* Shadow registers. */ + WRT_REG_DWORD(®->iobase_addr, 0x0F70); + RD_REG_DWORD(®->iobase_addr); + WRT_REG_DWORD(®->iobase_select, 0xB0000000); + fw->shadow_reg[0] = htonl(RD_REG_DWORD(®->iobase_sdata)); - mb[0] = RD_REG_WORD(®->mailbox0); - mb[2] = RD_REG_WORD(®->mailbox2); - mb[3] = RD_REG_WORD(®->mailbox3); + WRT_REG_DWORD(®->iobase_select, 0xB0100000); + fw->shadow_reg[1] = htonl(RD_REG_DWORD(®->iobase_sdata)); - WRT_REG_DWORD(®->hccr, - HCCRX_CLR_RISC_INT); - RD_REG_DWORD(®->hccr); - break; - } + WRT_REG_DWORD(®->iobase_select, 0xB0200000); + fw->shadow_reg[2] = htonl(RD_REG_DWORD(®->iobase_sdata)); - /* Clear this intr; it wasn't a mailbox intr */ - WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); - RD_REG_DWORD(®->hccr); - } + WRT_REG_DWORD(®->iobase_select, 0xB0300000); + fw->shadow_reg[3] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0400000); + fw->shadow_reg[4] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0500000); + fw->shadow_reg[5] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0600000); + fw->shadow_reg[6] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0700000); + fw->shadow_reg[7] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0800000); + fw->shadow_reg[8] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0900000); + fw->shadow_reg[9] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0A00000); + fw->shadow_reg[10] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + /* RISC I/O register. */ + WRT_REG_DWORD(®->iobase_addr, 0x0010); + RD_REG_DWORD(®->iobase_addr); + fw->risc_io_reg = htonl(RD_REG_DWORD(®->iobase_window)); + + /* Mailbox registers. */ + mbx_reg = ®->mailbox0; + for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++) + fw->mailbox_reg[cnt] = htons(RD_REG_WORD(mbx_reg++)); + + /* Transfer sequence registers. */ + iter_reg = fw->xseq_gp_reg; + WRT_REG_DWORD(®->iobase_addr, 0xBF00); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xBF10); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xBF20); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xBF30); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xBF40); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xBF50); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xBF60); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xBF70); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->xseq_0_reg; + WRT_REG_DWORD(®->iobase_addr, 0xBFC0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xBFD0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xBFE0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xBFF0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < sizeof(fw->xseq_1_reg) / 4; cnt++) + fw->xseq_1_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Receive sequence registers. */ + iter_reg = fw->rseq_gp_reg; + WRT_REG_DWORD(®->iobase_addr, 0xFF00); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xFF10); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xFF20); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xFF30); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xFF40); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xFF50); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xFF60); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xFF70); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->rseq_0_reg; + WRT_REG_DWORD(®->iobase_addr, 0xFFC0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xFFD0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xFFE0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < sizeof(fw->rseq_1_reg) / 4; cnt++) + fw->rseq_1_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xFFF0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < sizeof(fw->rseq_2_reg) / 4; cnt++) + fw->rseq_2_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Auxiliary sequence registers. */ + iter_reg = fw->aseq_gp_reg; + WRT_REG_DWORD(®->iobase_addr, 0xB000); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xB010); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xB020); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xB030); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xB040); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xB050); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xB060); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xB070); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->aseq_0_reg; + WRT_REG_DWORD(®->iobase_addr, 0xB0C0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xB0D0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xB0E0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < sizeof(fw->aseq_1_reg) / 4; cnt++) + fw->aseq_1_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0xB0F0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < sizeof(fw->aseq_2_reg) / 4; cnt++) + fw->aseq_2_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Command DMA registers. */ + WRT_REG_DWORD(®->iobase_addr, 0x7100); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < sizeof(fw->cmd_dma_reg) / 4; cnt++) + fw->cmd_dma_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Queues. */ + iter_reg = fw->req0_dma_reg; + WRT_REG_DWORD(®->iobase_addr, 0x7200); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 8; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->resp0_dma_reg; + WRT_REG_DWORD(®->iobase_addr, 0x7300); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 8; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->req1_dma_reg; + WRT_REG_DWORD(®->iobase_addr, 0x7400); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 8; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Transmit DMA registers. */ + iter_reg = fw->xmt0_dma_reg; + WRT_REG_DWORD(®->iobase_addr, 0x7600); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x7610); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->xmt1_dma_reg; + WRT_REG_DWORD(®->iobase_addr, 0x7620); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x7630); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->xmt2_dma_reg; + WRT_REG_DWORD(®->iobase_addr, 0x7640); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x7650); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->xmt3_dma_reg; + WRT_REG_DWORD(®->iobase_addr, 0x7660); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x7670); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->xmt4_dma_reg; + WRT_REG_DWORD(®->iobase_addr, 0x7680); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x7690); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x76A0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < sizeof(fw->xmt_data_dma_reg) / 4; cnt++) + fw->xmt_data_dma_reg[cnt] = + htonl(RD_REG_DWORD(dmp_reg++)); + + /* Receive DMA registers. */ + iter_reg = fw->rcvt0_data_dma_reg; + WRT_REG_DWORD(®->iobase_addr, 0x7700); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x7710); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->rcvt1_data_dma_reg; + WRT_REG_DWORD(®->iobase_addr, 0x7720); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x7730); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + /* RISC registers. */ + iter_reg = fw->risc_gp_reg; + WRT_REG_DWORD(®->iobase_addr, 0x0F00); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x0F10); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x0F20); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x0F30); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x0F40); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x0F50); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x0F60); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x0F70); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Local memory controller registers. */ + iter_reg = fw->lmc_reg; + WRT_REG_DWORD(®->iobase_addr, 0x3000); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x3010); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x3020); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x3030); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x3040); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x3050); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x3060); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x3070); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Fibre Protocol Module registers. */ + iter_reg = fw->fpm_hdw_reg; + WRT_REG_DWORD(®->iobase_addr, 0x4000); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x4010); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x4020); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x4030); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x4040); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x4050); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x4060); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x4070); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x4080); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x4090); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x40A0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x40B0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Frame Buffer registers. */ + iter_reg = fw->fb_hdw_reg; + WRT_REG_DWORD(®->iobase_addr, 0x6000); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x6010); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x6020); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x6030); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x6040); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x6100); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x6130); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x6150); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x6170); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x6190); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x61B0); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + WRT_REG_DWORD(®->iobase_addr, 0x6F00); + dmp_reg = ®->iobase_window; + for (cnt = 0; cnt < 16; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Reset RISC. */ + WRT_REG_DWORD(®->ctrl_status, + CSRX_DMA_SHUTDOWN|MWB_4096_BYTES); + for (cnt = 0; cnt < 30000; cnt++) { + if ((RD_REG_DWORD(®->ctrl_status) & + CSRX_DMA_ACTIVE) == 0) + break; + + udelay(10); + } + + WRT_REG_DWORD(®->ctrl_status, + CSRX_ISP_SOFT_RESET|CSRX_DMA_SHUTDOWN|MWB_4096_BYTES); + pci_read_config_word(ha->pdev, PCI_COMMAND, &wd); + + udelay(100); + /* Wait for firmware to complete NVRAM accesses. */ + mb0 = (uint32_t) RD_REG_WORD(®->mailbox0); + for (cnt = 10000 ; cnt && mb0; cnt--) { udelay(5); + mb0 = (uint32_t) RD_REG_WORD(®->mailbox0); + barrier(); } - if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { - rval = mb[0] & MBS_MASK; - fw->ext_mem[cnt] = htonl((mb[3] << 16) | mb[2]); - } else { - rval = QLA_FUNCTION_FAILED; + /* Wait for soft-reset to complete. */ + for (cnt = 0; cnt < 30000; cnt++) { + if ((RD_REG_DWORD(®->ctrl_status) & + CSRX_ISP_SOFT_RESET) == 0) + break; + + udelay(10); } + WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_RESET); + RD_REG_DWORD(®->hccr); /* PCI Posting. */ } + for (cnt = 30000; RD_REG_WORD(®->mailbox0) != 0 && + rval == QLA_SUCCESS; cnt--) { + if (cnt) + udelay(100); + else + rval = QLA_FUNCTION_TIMEOUT; + } + + if (rval == QLA_SUCCESS) + rval = qla2xxx_dump_memory(ha, fw->code_ram, + sizeof(fw->code_ram), fw->ext_mem, &nxt); + if (rval == QLA_SUCCESS) { - eft = qla2xxx_copy_queues(ha, &fw->ext_mem[cnt]); + nxt = qla2xxx_copy_queues(ha, nxt); if (ha->eft) - memcpy(eft, ha->eft, ntohl(ha->fw_dump->eft_size)); + memcpy(nxt, ha->eft, ntohl(ha->fw_dump->eft_size)); } if (rval != QLA_SUCCESS) { @@ -1332,7 +2038,7 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) ha->fw_dumped = 1; } -qla24xx_fw_dump_failed: +qla25xx_fw_dump_failed: if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); } diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index 49dffeb7851..cca4b0d8253 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.h +++ b/drivers/scsi/qla2xxx/qla_dbg.h @@ -213,6 +213,43 @@ struct qla24xx_fw_dump { uint32_t ext_mem[1]; }; +struct qla25xx_fw_dump { + uint32_t host_status; + uint32_t host_reg[32]; + uint32_t shadow_reg[11]; + uint32_t risc_io_reg; + uint16_t mailbox_reg[32]; + uint32_t xseq_gp_reg[128]; + uint32_t xseq_0_reg[48]; + uint32_t xseq_1_reg[16]; + uint32_t rseq_gp_reg[128]; + uint32_t rseq_0_reg[32]; + uint32_t rseq_1_reg[16]; + uint32_t rseq_2_reg[16]; + uint32_t aseq_gp_reg[128]; + uint32_t aseq_0_reg[32]; + uint32_t aseq_1_reg[16]; + uint32_t aseq_2_reg[16]; + uint32_t cmd_dma_reg[16]; + uint32_t req0_dma_reg[15]; + uint32_t resp0_dma_reg[15]; + uint32_t req1_dma_reg[15]; + uint32_t xmt0_dma_reg[32]; + uint32_t xmt1_dma_reg[32]; + uint32_t xmt2_dma_reg[32]; + uint32_t xmt3_dma_reg[32]; + uint32_t xmt4_dma_reg[32]; + uint32_t xmt_data_dma_reg[16]; + uint32_t rcvt0_data_dma_reg[32]; + uint32_t rcvt1_data_dma_reg[32]; + uint32_t risc_gp_reg[128]; + uint32_t lmc_reg[128]; + uint32_t fpm_hdw_reg[192]; + uint32_t fb_hdw_reg[192]; + uint32_t code_ram[0x2000]; + uint32_t ext_mem[1]; +}; + #define EFT_NUM_BUFFERS 4 #define EFT_BYTES_PER_BUFFER 0x4000 #define EFT_SIZE ((EFT_BYTES_PER_BUFFER) * (EFT_NUM_BUFFERS)) @@ -246,5 +283,6 @@ struct qla2xxx_fw_dump { struct qla2100_fw_dump isp21; struct qla2300_fw_dump isp23; struct qla24xx_fw_dump isp24; + struct qla25xx_fw_dump isp25; } isp; }; diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index a1ca590ba44..0c9f36c8a24 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -1711,6 +1711,14 @@ struct ct_fdmi_hba_attributes { #define FDMI_PORT_OS_DEVICE_NAME 5 #define FDMI_PORT_HOST_NAME 6 +#define FDMI_PORT_SPEED_1GB 0x1 +#define FDMI_PORT_SPEED_2GB 0x2 +#define FDMI_PORT_SPEED_10GB 0x4 +#define FDMI_PORT_SPEED_4GB 0x8 +#define FDMI_PORT_SPEED_8GB 0x10 +#define FDMI_PORT_SPEED_16GB 0x20 +#define FDMI_PORT_SPEED_UNKNOWN 0x8000 + struct ct_fdmi_port_attr { uint16_t type; uint16_t len; @@ -2201,6 +2209,7 @@ typedef struct scsi_qla_host { #define SWITCH_FOUND BIT_3 #define DFLG_NO_CABLE BIT_4 +#define PCI_DEVICE_ID_QLOGIC_ISP2532 0x2532 uint32_t device_type; #define DT_ISP2100 BIT_0 #define DT_ISP2200 BIT_1 @@ -2213,8 +2222,11 @@ typedef struct scsi_qla_host { #define DT_ISP2432 BIT_8 #define DT_ISP5422 BIT_9 #define DT_ISP5432 BIT_10 -#define DT_ISP_LAST (DT_ISP5432 << 1) +#define DT_ISP2532 BIT_11 +#define DT_ISP_LAST (DT_ISP2532 << 1) +#define DT_IIDMA BIT_26 +#define DT_FWI2 BIT_27 #define DT_ZIO_SUPPORTED BIT_28 #define DT_OEM_001 BIT_29 #define DT_ISP2200A BIT_30 @@ -2232,12 +2244,16 @@ typedef struct scsi_qla_host { #define IS_QLA2432(ha) (DT_MASK(ha) & DT_ISP2432) #define IS_QLA5422(ha) (DT_MASK(ha) & DT_ISP5422) #define IS_QLA5432(ha) (DT_MASK(ha) & DT_ISP5432) +#define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532) #define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \ IS_QLA6312(ha) || IS_QLA6322(ha)) #define IS_QLA24XX(ha) (IS_QLA2422(ha) || IS_QLA2432(ha)) #define IS_QLA54XX(ha) (IS_QLA5422(ha) || IS_QLA5432(ha)) +#define IS_QLA25XX(ha) (IS_QLA2532(ha)) +#define IS_IIDMA_CAPABLE(ha) ((ha)->device_type & DT_IIDMA) +#define IS_FWI2_CAPABLE(ha) ((ha)->device_type & DT_FWI2) #define IS_ZIO_SUPPORTED(ha) ((ha)->device_type & DT_ZIO_SUPPORTED) #define IS_OEM_001(ha) ((ha)->device_type & DT_OEM_001) #define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS) @@ -2274,7 +2290,7 @@ typedef struct scsi_qla_host { uint16_t rsp_ring_index; /* Current index. */ uint16_t response_q_length; - struct isp_operations isp_ops; + struct isp_operations *isp_ops; /* Outstandings ISP commands. */ srb_t *outstanding_cmds[MAX_OUTSTANDING_COMMANDS]; @@ -2298,6 +2314,7 @@ typedef struct scsi_qla_host { #define PORT_SPEED_1GB 0x00 #define PORT_SPEED_2GB 0x01 #define PORT_SPEED_4GB 0x03 +#define PORT_SPEED_8GB 0x04 uint16_t link_data_rate; /* F/W operating speed */ uint8_t current_topology; @@ -2564,6 +2581,7 @@ typedef struct scsi_qla_host { #define OPTROM_SIZE_2300 0x20000 #define OPTROM_SIZE_2322 0x100000 #define OPTROM_SIZE_24XX 0x100000 +#define OPTROM_SIZE_25XX 0x200000 #include "qla_gbl.h" #include "qla_dbg.h" diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 63a11fef5d1..99fe49618d6 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -8,14 +8,17 @@ #define __QLA_FW_H #define MBS_CHECKSUM_ERROR 0x4010 +#define MBS_INVALID_PRODUCT_KEY 0x4020 /* * Firmware Options. */ #define FO1_ENABLE_PUREX BIT_10 #define FO1_DISABLE_LED_CTRL BIT_6 +#define FO1_ENABLE_8016 BIT_0 #define FO2_ENABLE_SEL_CLASS2 BIT_5 #define FO3_NO_ABTS_ON_LINKDOWN BIT_14 +#define FO3_HOLD_STS_IOCB BIT_12 /* * Port Database structure definition for ISP 24xx. @@ -341,7 +344,9 @@ struct init_cb_24xx { * BIT 10 = Reserved * BIT 11 = Enable FC-SP Security * BIT 12 = FC Tape Enable - * BIT 13-31 = Reserved + * BIT 13 = Reserved + * BIT 14 = Enable Target PRLI Control + * BIT 15-31 = Reserved */ uint32_t firmware_options_2; @@ -363,7 +368,8 @@ struct init_cb_24xx { * BIT 13 = Data Rate bit 0 * BIT 14 = Data Rate bit 1 * BIT 15 = Data Rate bit 2 - * BIT 16-31 = Reserved + * BIT 16 = Enable 75 ohm Termination Select + * BIT 17-31 = Reserved */ uint32_t firmware_options_3; @@ -435,6 +441,7 @@ struct cmd_type_7 { #define TMF_LUN_RESET BIT_12 #define TMF_CLEAR_TASK_SET BIT_10 #define TMF_ABORT_TASK_SET BIT_9 +#define TMF_DSD_LIST_ENABLE BIT_2 #define TMF_READ_DATA BIT_1 #define TMF_WRITE_DATA BIT_0 @@ -589,7 +596,7 @@ struct els_entry_24xx { #define EST_SOFI3 (1 << 4) #define EST_SOFI2 (3 << 4) - uint32_t rx_xchg_address[2]; /* Receive exchange address. */ + uint32_t rx_xchg_address; /* Receive exchange address. */ uint16_t rx_dsd_count; uint8_t opcode; @@ -650,6 +657,7 @@ struct logio_entry_24xx { uint16_t control_flags; /* Control flags. */ /* Modifiers. */ +#define LCF_INCLUDE_SNS BIT_10 /* Include SNS (FFFFFC) during LOGO. */ #define LCF_FCP2_OVERRIDE BIT_9 /* Set/Reset word 3 of PRLI. */ #define LCF_CLASS_2 BIT_8 /* Enable class 2 during PLOGI. */ #define LCF_FREE_NPORT BIT_7 /* Release NPORT handle after LOGO. */ @@ -779,6 +787,15 @@ struct device_reg_24xx { #define FA_RISC_CODE_ADDR 0x20000 #define FA_RISC_CODE_SEGMENTS 2 +#define FA_FW_AREA_ADDR 0x40000 +#define FA_VPD_NVRAM_ADDR 0x48000 +#define FA_FEATURE_ADDR 0x4C000 +#define FA_FLASH_DESCR_ADDR 0x50000 +#define FA_HW_EVENT_ADDR 0x54000 +#define FA_BOOT_LOG_ADDR 0x58000 +#define FA_FW_DUMP0_ADDR 0x60000 +#define FA_FW_DUMP1_ADDR 0x70000 + uint32_t flash_data; /* Flash/NVRAM BIOS data. */ uint32_t ctrl_status; /* Control/Status. */ @@ -859,10 +876,13 @@ struct device_reg_24xx { #define HCCRX_CLR_RISC_INT 0xA0000000 uint32_t gpiod; /* GPIO Data register. */ + /* LED update mask. */ #define GPDX_LED_UPDATE_MASK (BIT_20|BIT_19|BIT_18) /* Data update mask. */ #define GPDX_DATA_UPDATE_MASK (BIT_17|BIT_16) + /* Data update mask. */ +#define GPDX_DATA_UPDATE_2_MASK (BIT_28|BIT_27|BIT_26|BIT_17|BIT_16) /* LED control mask. */ #define GPDX_LED_COLOR_MASK (BIT_4|BIT_3|BIT_2) /* LED bit values. Color names as @@ -877,6 +897,8 @@ struct device_reg_24xx { uint32_t gpioe; /* GPIO Enable register. */ /* Enable update mask. */ #define GPEX_ENABLE_UPDATE_MASK (BIT_17|BIT_16) + /* Enable update mask. */ +#define GPEX_ENABLE_UPDATE_2_MASK (BIT_28|BIT_27|BIT_26|BIT_17|BIT_16) /* Enable. */ #define GPEX_ENABLE (BIT_1|BIT_0) @@ -916,6 +938,14 @@ struct device_reg_24xx { uint16_t mailbox29; uint16_t mailbox30; uint16_t mailbox31; + + uint32_t iobase_window; + uint32_t unused_4[8]; /* Gap. */ + uint32_t iobase_q; + uint32_t unused_5[2]; /* Gap. */ + uint32_t iobase_select; + uint32_t unused_6[2]; /* Gap. */ + uint32_t iobase_sdata; }; /* MID Support ***************************************************************/ diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index b44eff2803c..aa1e4115228 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -17,6 +17,7 @@ extern int qla2x00_initialize_adapter(scsi_qla_host_t *); extern int qla2100_pci_config(struct scsi_qla_host *); extern int qla2300_pci_config(struct scsi_qla_host *); extern int qla24xx_pci_config(scsi_qla_host_t *); +extern int qla25xx_pci_config(scsi_qla_host_t *); extern void qla2x00_reset_chip(struct scsi_qla_host *); extern void qla24xx_reset_chip(struct scsi_qla_host *); extern int qla2x00_chip_diag(struct scsi_qla_host *); @@ -281,6 +282,10 @@ extern int qla2x00_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, uint32_t); extern int qla24xx_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, uint32_t); +extern uint8_t *qla25xx_read_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, + uint32_t); +extern int qla25xx_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, + uint32_t); extern int qla2x00_beacon_on(struct scsi_qla_host *); extern int qla2x00_beacon_off(struct scsi_qla_host *); @@ -307,6 +312,7 @@ extern int qla24xx_get_flash_version(scsi_qla_host_t *, void *); extern void qla2100_fw_dump(scsi_qla_host_t *, int); extern void qla2300_fw_dump(scsi_qla_host_t *, int); extern void qla24xx_fw_dump(scsi_qla_host_t *, int); +extern void qla25xx_fw_dump(scsi_qla_host_t *, int); extern void qla2x00_dump_regs(scsi_qla_host_t *); extern void qla2x00_dump_buffer(uint8_t *, uint32_t); extern void qla2x00_print_scsi_cmd(struct scsi_cmnd *); diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index a086b3f0df6..b06cbb8580d 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -127,7 +127,7 @@ qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt, DEBUG2_3(printk("scsi(%ld): %s failed, error status (%x).\n", ha->host_no, routine, ms_pkt->entry_status)); } else { - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) comp_status = le16_to_cpu( ((struct ct_entry_24xx *)ms_pkt)->comp_status); else @@ -180,7 +180,8 @@ qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport) /* Issue GA_NXT */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops.prep_ms_iocb(ha, GA_NXT_REQ_SIZE, GA_NXT_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GA_NXT_REQ_SIZE, + GA_NXT_RSP_SIZE); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GA_NXT_CMD, @@ -266,7 +267,8 @@ qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list) /* Issue GID_PT */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops.prep_ms_iocb(ha, GID_PT_REQ_SIZE, GID_PT_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GID_PT_REQ_SIZE, + GID_PT_RSP_SIZE); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GID_PT_CMD, @@ -338,7 +340,7 @@ qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list) for (i = 0; i < MAX_FIBRE_DEVICES; i++) { /* Issue GPN_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops.prep_ms_iocb(ha, GPN_ID_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GPN_ID_REQ_SIZE, GPN_ID_RSP_SIZE); /* Prepare CT request */ @@ -399,7 +401,7 @@ qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list) for (i = 0; i < MAX_FIBRE_DEVICES; i++) { /* Issue GNN_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops.prep_ms_iocb(ha, GNN_ID_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GNN_ID_REQ_SIZE, GNN_ID_RSP_SIZE); /* Prepare CT request */ @@ -473,7 +475,8 @@ qla2x00_rft_id(scsi_qla_host_t *ha) /* Issue RFT_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops.prep_ms_iocb(ha, RFT_ID_REQ_SIZE, RFT_ID_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RFT_ID_REQ_SIZE, + RFT_ID_RSP_SIZE); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFT_ID_CMD, @@ -528,7 +531,8 @@ qla2x00_rff_id(scsi_qla_host_t *ha) /* Issue RFF_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops.prep_ms_iocb(ha, RFF_ID_REQ_SIZE, RFF_ID_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RFF_ID_REQ_SIZE, + RFF_ID_RSP_SIZE); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFF_ID_CMD, @@ -582,7 +586,8 @@ qla2x00_rnn_id(scsi_qla_host_t *ha) /* Issue RNN_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops.prep_ms_iocb(ha, RNN_ID_REQ_SIZE, RNN_ID_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RNN_ID_REQ_SIZE, + RNN_ID_RSP_SIZE); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RNN_ID_CMD, @@ -645,7 +650,7 @@ qla2x00_rsnn_nn(scsi_qla_host_t *ha) /* Issue RSNN_NN */ /* Prepare common MS IOCB */ /* Request size adjusted after CT preparation */ - ms_pkt = ha->isp_ops.prep_ms_iocb(ha, 0, RSNN_NN_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(ha, 0, RSNN_NN_RSP_SIZE); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RSNN_NN_CMD, @@ -1102,7 +1107,7 @@ qla2x00_mgmt_svr_login(scsi_qla_host_t *ha) if (ha->flags.management_server_logged_in) return ret; - ha->isp_ops.fabric_login(ha, ha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa, + ha->isp_ops->fabric_login(ha, ha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa, mb, BIT_1); if (mb[0] != MBS_COMMAND_COMPLETE) { DEBUG2_13(printk("%s(%ld): Failed MANAGEMENT_SERVER login: " @@ -1198,7 +1203,7 @@ qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size) ms_iocb_entry_t *ms_pkt = ha->ms_iocb; struct ct_entry_24xx *ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { ct_pkt->cmd_byte_count = cpu_to_le32(req_size); ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count; } else { @@ -1253,7 +1258,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha) /* Issue RHBA */ /* Prepare common MS IOCB */ /* Request size adjusted after CT preparation */ - ms_pkt = ha->isp_ops.prep_ms_fdmi_iocb(ha, 0, RHBA_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, 0, RHBA_RSP_SIZE); /* Prepare CT request */ ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RHBA_CMD, @@ -1373,7 +1378,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha) /* Firmware version */ eiter = (struct ct_fdmi_hba_attr *) (entries + size); eiter->type = __constant_cpu_to_be16(FDMI_HBA_FIRMWARE_VERSION); - ha->isp_ops.fw_version_str(ha, eiter->a.fw_version); + ha->isp_ops->fw_version_str(ha, eiter->a.fw_version); alen = strlen(eiter->a.fw_version); alen += (alen & 3) ? (4 - (alen & 3)) : 4; eiter->len = cpu_to_be16(4 + alen); @@ -1439,7 +1444,7 @@ qla2x00_fdmi_dhba(scsi_qla_host_t *ha) /* Issue RPA */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops.prep_ms_fdmi_iocb(ha, DHBA_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, DHBA_REQ_SIZE, DHBA_RSP_SIZE); /* Prepare CT request */ @@ -1497,7 +1502,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) /* Issue RPA */ /* Prepare common MS IOCB */ /* Request size adjusted after CT preparation */ - ms_pkt = ha->isp_ops.prep_ms_fdmi_iocb(ha, 0, RPA_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, 0, RPA_RSP_SIZE); /* Prepare CT request */ ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RPA_CMD, @@ -1527,12 +1532,20 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) eiter = (struct ct_fdmi_port_attr *) (entries + size); eiter->type = __constant_cpu_to_be16(FDMI_PORT_SUPPORT_SPEED); eiter->len = __constant_cpu_to_be16(4 + 4); - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) - eiter->a.sup_speed = __constant_cpu_to_be32(4); + if (IS_QLA25XX(ha)) + eiter->a.sup_speed = __constant_cpu_to_be32( + FDMI_PORT_SPEED_1GB|FDMI_PORT_SPEED_2GB| + FDMI_PORT_SPEED_4GB|FDMI_PORT_SPEED_8GB); + else if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + eiter->a.sup_speed = __constant_cpu_to_be32( + FDMI_PORT_SPEED_1GB|FDMI_PORT_SPEED_2GB| + FDMI_PORT_SPEED_4GB); else if (IS_QLA23XX(ha)) - eiter->a.sup_speed = __constant_cpu_to_be32(2); + eiter->a.sup_speed =__constant_cpu_to_be32( + FDMI_PORT_SPEED_1GB|FDMI_PORT_SPEED_2GB); else - eiter->a.sup_speed = __constant_cpu_to_be32(1); + eiter->a.sup_speed = __constant_cpu_to_be32( + FDMI_PORT_SPEED_1GB); size += 4 + 4; DEBUG13(printk("%s(%ld): SUPPORTED_SPEED=%x.\n", __func__, ha->host_no, @@ -1543,14 +1556,25 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) eiter->type = __constant_cpu_to_be16(FDMI_PORT_CURRENT_SPEED); eiter->len = __constant_cpu_to_be16(4 + 4); switch (ha->link_data_rate) { - case 0: - eiter->a.cur_speed = __constant_cpu_to_be32(1); + case PORT_SPEED_1GB: + eiter->a.cur_speed = + __constant_cpu_to_be32(FDMI_PORT_SPEED_1GB); + break; + case PORT_SPEED_2GB: + eiter->a.cur_speed = + __constant_cpu_to_be32(FDMI_PORT_SPEED_2GB); + break; + case PORT_SPEED_4GB: + eiter->a.cur_speed = + __constant_cpu_to_be32(FDMI_PORT_SPEED_4GB); break; - case 1: - eiter->a.cur_speed = __constant_cpu_to_be32(2); + case PORT_SPEED_8GB: + eiter->a.cur_speed = + __constant_cpu_to_be32(FDMI_PORT_SPEED_8GB); break; - case 3: - eiter->a.cur_speed = __constant_cpu_to_be32(4); + default: + eiter->a.cur_speed = + __constant_cpu_to_be32(FDMI_PORT_SPEED_UNKNOWN); break; } size += 4 + 4; @@ -1562,7 +1586,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) eiter = (struct ct_fdmi_port_attr *) (entries + size); eiter->type = __constant_cpu_to_be16(FDMI_PORT_MAX_FRAME_SIZE); eiter->len = __constant_cpu_to_be16(4 + 4); - max_frame_size = IS_QLA24XX(ha) || IS_QLA54XX(ha) ? + max_frame_size = IS_FWI2_CAPABLE(ha) ? (uint32_t) icb24->frame_payload_size: (uint32_t) ha->init_cb->frame_payload_size; eiter->a.max_frame_size = cpu_to_be32(max_frame_size); @@ -1678,7 +1702,7 @@ qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list) struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; - if (!IS_QLA24XX(ha) && !IS_QLA54XX(ha)) + if (!IS_IIDMA_CAPABLE(ha)) return QLA_FUNCTION_FAILED; for (i = 0; i < MAX_FIBRE_DEVICES; i++) { @@ -1686,7 +1710,7 @@ qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list) memset(list[i].fabric_port_name, 0, WWN_SIZE); /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops.prep_ms_iocb(ha, GFPN_ID_REQ_SIZE, + ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GFPN_ID_REQ_SIZE, GFPN_ID_RSP_SIZE); /* Prepare CT request */ @@ -1786,7 +1810,7 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list) struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; - if (!IS_QLA24XX(ha) && !IS_QLA54XX(ha)) + if (!IS_IIDMA_CAPABLE(ha)) return QLA_FUNCTION_FAILED; if (!ha->flags.gpsc_supported) return QLA_FUNCTION_FAILED; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index cc6ebb609e9..5ec798c2bf1 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -79,20 +79,20 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags); qla_printk(KERN_INFO, ha, "Configuring PCI space...\n"); - rval = ha->isp_ops.pci_config(ha); + rval = ha->isp_ops->pci_config(ha); if (rval) { DEBUG2(printk("scsi(%ld): Unable to configure PCI space.\n", ha->host_no)); return (rval); } - ha->isp_ops.reset_chip(ha); + ha->isp_ops->reset_chip(ha); - ha->isp_ops.get_flash_version(ha, ha->request_ring); + ha->isp_ops->get_flash_version(ha, ha->request_ring); qla_printk(KERN_INFO, ha, "Configure NVRAM parameters...\n"); - ha->isp_ops.nvram_config(ha); + ha->isp_ops->nvram_config(ha); if (ha->flags.disable_serdes) { /* Mask HBA via NVRAM settings? */ @@ -108,7 +108,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) qla_printk(KERN_INFO, ha, "Verifying loaded RISC code...\n"); if (qla2x00_isp_firmware(ha) != QLA_SUCCESS) { - rval = ha->isp_ops.chip_diag(ha); + rval = ha->isp_ops->chip_diag(ha); if (rval) return (rval); rval = qla2x00_setup_chip(ha); @@ -129,14 +129,13 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) int qla2100_pci_config(scsi_qla_host_t *ha) { - int ret; uint16_t w; uint32_t d; unsigned long flags; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; pci_set_master(ha->pdev); - ret = pci_set_mwi(ha->pdev); + pci_try_set_mwi(ha->pdev); pci_read_config_word(ha->pdev, PCI_COMMAND, &w); w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); @@ -164,7 +163,6 @@ qla2100_pci_config(scsi_qla_host_t *ha) int qla2300_pci_config(scsi_qla_host_t *ha) { - int ret; uint16_t w; uint32_t d; unsigned long flags = 0; @@ -172,7 +170,7 @@ qla2300_pci_config(scsi_qla_host_t *ha) struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; pci_set_master(ha->pdev); - ret = pci_set_mwi(ha->pdev); + pci_try_set_mwi(ha->pdev); pci_read_config_word(ha->pdev, PCI_COMMAND, &w); w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); @@ -250,15 +248,13 @@ qla2300_pci_config(scsi_qla_host_t *ha) int qla24xx_pci_config(scsi_qla_host_t *ha) { - int ret; uint16_t w; uint32_t d; unsigned long flags = 0; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - int pcix_cmd_reg, pcie_dctl_reg; pci_set_master(ha->pdev); - ret = pci_set_mwi(ha->pdev); + pci_try_set_mwi(ha->pdev); pci_read_config_word(ha->pdev, PCI_COMMAND, &w); w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); @@ -268,28 +264,12 @@ qla24xx_pci_config(scsi_qla_host_t *ha) pci_write_config_byte(ha->pdev, PCI_LATENCY_TIMER, 0x80); /* PCI-X -- adjust Maximum Memory Read Byte Count (2048). */ - pcix_cmd_reg = pci_find_capability(ha->pdev, PCI_CAP_ID_PCIX); - if (pcix_cmd_reg) { - uint16_t pcix_cmd; - - pcix_cmd_reg += PCI_X_CMD; - pci_read_config_word(ha->pdev, pcix_cmd_reg, &pcix_cmd); - pcix_cmd &= ~PCI_X_CMD_MAX_READ; - pcix_cmd |= 0x0008; - pci_write_config_word(ha->pdev, pcix_cmd_reg, pcix_cmd); - } + if (pci_find_capability(ha->pdev, PCI_CAP_ID_PCIX)) + pcix_set_mmrbc(ha->pdev, 2048); /* PCIe -- adjust Maximum Read Request Size (2048). */ - pcie_dctl_reg = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP); - if (pcie_dctl_reg) { - uint16_t pcie_dctl; - - pcie_dctl_reg += PCI_EXP_DEVCTL; - pci_read_config_word(ha->pdev, pcie_dctl_reg, &pcie_dctl); - pcie_dctl &= ~PCI_EXP_DEVCTL_READRQ; - pcie_dctl |= 0x4000; - pci_write_config_word(ha->pdev, pcie_dctl_reg, pcie_dctl); - } + if (pci_find_capability(ha->pdev, PCI_CAP_ID_EXP)) + pcie_set_readrq(ha->pdev, 2048); /* Reset expansion ROM address decode enable */ pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); @@ -307,6 +287,40 @@ qla24xx_pci_config(scsi_qla_host_t *ha) } /** + * qla25xx_pci_config() - Setup ISP25xx PCI configuration registers. + * @ha: HA context + * + * Returns 0 on success. + */ +int +qla25xx_pci_config(scsi_qla_host_t *ha) +{ + uint16_t w; + uint32_t d; + + pci_set_master(ha->pdev); + pci_try_set_mwi(ha->pdev); + + pci_read_config_word(ha->pdev, PCI_COMMAND, &w); + w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); + w &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(ha->pdev, PCI_COMMAND, w); + + /* PCIe -- adjust Maximum Read Request Size (2048). */ + if (pci_find_capability(ha->pdev, PCI_CAP_ID_EXP)) + pcie_set_readrq(ha->pdev, 2048); + + /* Reset expansion ROM address decode enable */ + pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); + d &= ~PCI_ROM_ADDRESS_ENABLE; + pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + + ha->chip_revision = ha->pdev->revision; + + return QLA_SUCCESS; +} + +/** * qla2x00_isp_firmware() - Choose firmware image. * @ha: HA context * @@ -351,7 +365,7 @@ qla2x00_reset_chip(scsi_qla_host_t *ha) uint32_t cnt; uint16_t cmd; - ha->isp_ops.disable_intrs(ha); + ha->isp_ops->disable_intrs(ha); spin_lock_irqsave(&ha->hardware_lock, flags); @@ -551,7 +565,7 @@ qla24xx_reset_risc(scsi_qla_host_t *ha) void qla24xx_reset_chip(scsi_qla_host_t *ha) { - ha->isp_ops.disable_intrs(ha); + ha->isp_ops->disable_intrs(ha); /* Perform RISC reset. */ qla24xx_reset_risc(ha); @@ -736,8 +750,10 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha) fixed_size = offsetof(struct qla2300_fw_dump, data_ram); mem_size = (ha->fw_memory_size - 0x11000 + 1) * sizeof(uint16_t); - } else if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { - fixed_size = offsetof(struct qla24xx_fw_dump, ext_mem); + } else if (IS_FWI2_CAPABLE(ha)) { + fixed_size = IS_QLA25XX(ha) ? + offsetof(struct qla25xx_fw_dump, ext_mem): + offsetof(struct qla24xx_fw_dump, ext_mem); mem_size = (ha->fw_memory_size - 0x100000 + 1) * sizeof(uint32_t); @@ -879,7 +895,7 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) uint32_t srisc_address = 0; /* Load firmware sequences */ - rval = ha->isp_ops.load_risc(ha, &srisc_address); + rval = ha->isp_ops->load_risc(ha, &srisc_address); if (rval == QLA_SUCCESS) { DEBUG(printk("scsi(%ld): Verifying Checksum of loaded RISC " "code.\n", ha->host_no)); @@ -1130,12 +1146,12 @@ qla2x00_init_rings(scsi_qla_host_t *ha) /* Initialize response queue entries */ qla2x00_init_response_q_entries(ha); - ha->isp_ops.config_rings(ha); + ha->isp_ops->config_rings(ha); spin_unlock_irqrestore(&ha->hardware_lock, flags); /* Update any ISP specific firmware options before initialization. */ - ha->isp_ops.update_fw_options(ha); + ha->isp_ops->update_fw_options(ha); DEBUG(printk("scsi(%ld): Issue init firmware.\n", ha->host_no)); @@ -1459,7 +1475,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) ha->nvram_base = 0x80; /* Get NVRAM data and calculate checksum. */ - ha->isp_ops.read_nvram(ha, ptr, ha->nvram_base, ha->nvram_size); + ha->isp_ops->read_nvram(ha, ptr, ha->nvram_base, ha->nvram_size); for (cnt = 0, chksum = 0; cnt < ha->nvram_size; cnt++) chksum += *ptr++; @@ -2119,7 +2135,7 @@ qla2x00_iidma_fcport(scsi_qla_host_t *ha, fc_port_t *fcport) int rval; uint16_t port_speed, mb[6]; - if (!IS_QLA24XX(ha)) + if (!IS_IIDMA_CAPABLE(ha)) return; switch (be16_to_cpu(fcport->fp_speed)) { @@ -2267,7 +2283,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) scsi_qla_host_t *pha = to_qla_parent(ha); /* If FL port exists, then SNS is present */ - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) loop_id = NPH_F_PORT; else loop_id = SNS_FL_PORT; @@ -2294,11 +2310,11 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) qla2x00_fdmi_register(ha); /* Ensure we are logged into the SNS. */ - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) loop_id = NPH_SNS; else loop_id = SIMPLE_NAME_SERVER; - ha->isp_ops.fabric_login(ha, loop_id, 0xff, 0xff, + ha->isp_ops->fabric_login(ha, loop_id, 0xff, 0xff, 0xfc, mb, BIT_1 | BIT_0); if (mb[0] != MBS_COMMAND_COMPLETE) { DEBUG2(qla_printk(KERN_INFO, ha, @@ -2355,7 +2371,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) (fcport->flags & FCF_TAPE_PRESENT) == 0 && fcport->port_type != FCT_INITIATOR && fcport->port_type != FCT_BROADCAST) { - ha->isp_ops.fabric_logout(ha, + ha->isp_ops->fabric_logout(ha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, @@ -2664,7 +2680,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) (fcport->flags & FCF_TAPE_PRESENT) == 0 && fcport->port_type != FCT_INITIATOR && fcport->port_type != FCT_BROADCAST) { - ha->isp_ops.fabric_logout(ha, fcport->loop_id, + ha->isp_ops->fabric_logout(ha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); fcport->loop_id = FC_NO_LOOP_ID; @@ -2919,7 +2935,7 @@ qla2x00_fabric_dev_login(scsi_qla_host_t *ha, fc_port_t *fcport, opts |= BIT_1; rval = qla2x00_get_port_database(ha, fcport, opts); if (rval != QLA_SUCCESS) { - ha->isp_ops.fabric_logout(ha, fcport->loop_id, + ha->isp_ops->fabric_logout(ha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); qla2x00_mark_device_lost(ha, fcport, 1, 0); @@ -2964,7 +2980,7 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, fcport->d_id.b.area, fcport->d_id.b.al_pa)); /* Login fcport on switch. */ - ha->isp_ops.fabric_login(ha, fcport->loop_id, + ha->isp_ops->fabric_login(ha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa, mb, BIT_0); if (mb[0] == MBS_PORT_ID_USED) { @@ -3032,7 +3048,7 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, * dead. */ *next_loopid = fcport->loop_id; - ha->isp_ops.fabric_logout(ha, fcport->loop_id, + ha->isp_ops->fabric_logout(ha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); qla2x00_mark_device_lost(ha, fcport, 1, 0); @@ -3050,7 +3066,7 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, fcport->d_id.b.al_pa, fcport->loop_id, jiffies)); *next_loopid = fcport->loop_id; - ha->isp_ops.fabric_logout(ha, fcport->loop_id, + ha->isp_ops->fabric_logout(ha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); fcport->loop_id = FC_NO_LOOP_ID; @@ -3206,7 +3222,7 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) qla_printk(KERN_INFO, ha, "Performing ISP error recovery - ha= %p.\n", ha); - ha->isp_ops.reset_chip(ha); + ha->isp_ops->reset_chip(ha); atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { @@ -3232,9 +3248,9 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) } spin_unlock_irqrestore(&ha->hardware_lock, flags); - ha->isp_ops.get_flash_version(ha, ha->request_ring); + ha->isp_ops->get_flash_version(ha, ha->request_ring); - ha->isp_ops.nvram_config(ha); + ha->isp_ops->nvram_config(ha); if (!qla2x00_restart_isp(ha)) { clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); @@ -3249,7 +3265,7 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) ha->flags.online = 1; - ha->isp_ops.enable_intrs(ha); + ha->isp_ops->enable_intrs(ha); ha->isp_abort_cnt = 0; clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags); @@ -3274,7 +3290,7 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) * The next call disables the board * completely. */ - ha->isp_ops.reset_adapter(ha); + ha->isp_ops->reset_adapter(ha); ha->flags.online = 0; clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags); @@ -3331,7 +3347,7 @@ qla2x00_restart_isp(scsi_qla_host_t *ha) /* If firmware needs to be loaded */ if (qla2x00_isp_firmware(ha)) { ha->flags.online = 0; - if (!(status = ha->isp_ops.chip_diag(ha))) { + if (!(status = ha->isp_ops->chip_diag(ha))) { if (IS_QLA2100(ha) || IS_QLA2200(ha)) { status = qla2x00_setup_chip(ha); goto done; @@ -3423,7 +3439,7 @@ qla2x00_reset_adapter(scsi_qla_host_t *ha) struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; ha->flags.online = 0; - ha->isp_ops.disable_intrs(ha); + ha->isp_ops->disable_intrs(ha); spin_lock_irqsave(&ha->hardware_lock, flags); WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); @@ -3440,7 +3456,7 @@ qla24xx_reset_adapter(scsi_qla_host_t *ha) struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; ha->flags.online = 0; - ha->isp_ops.disable_intrs(ha); + ha->isp_ops->disable_intrs(ha); spin_lock_irqsave(&ha->hardware_lock, flags); WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_RESET); @@ -3498,7 +3514,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) /* Get NVRAM data and calculate checksum. */ dptr = (uint32_t *)nv; - ha->isp_ops.read_nvram(ha, (uint8_t *)dptr, ha->nvram_base, + ha->isp_ops->read_nvram(ha, (uint8_t *)dptr, ha->nvram_base, ha->nvram_size); for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++) chksum += le32_to_cpu(*dptr++); @@ -4012,7 +4028,7 @@ qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha) { int ret, retries; - if (!IS_QLA24XX(ha) && !IS_QLA54XX(ha)) + if (!IS_FWI2_CAPABLE(ha)) return; if (!ha->fw_major_version) return; diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index d3023338628..8e3b04464cf 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -104,7 +104,7 @@ static __inline__ void qla2x00_poll(scsi_qla_host_t *); static inline void qla2x00_poll(scsi_qla_host_t *ha) { - ha->isp_ops.intr_handler(0, ha); + ha->isp_ops->intr_handler(0, ha); } static __inline__ void qla2x00_check_fabric_devices(scsi_qla_host_t *); @@ -163,7 +163,7 @@ static inline int qla2x00_is_reserved_id(scsi_qla_host_t *, uint16_t); static inline int qla2x00_is_reserved_id(scsi_qla_host_t *ha, uint16_t loop_id) { - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) return (loop_id > NPH_LAST_HANDLE); return ((loop_id > ha->last_loop_id && loop_id < SNS_FIRST_LOOP_ID) || diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index c71863ff548..3a5e78cb6b3 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -326,7 +326,7 @@ qla2x00_start_scsi(srb_t *sp) tot_dsds = nseg; /* Calculate the number of request entries needed. */ - req_cnt = ha->isp_ops.calc_req_entries(tot_dsds); + req_cnt = ha->isp_ops->calc_req_entries(tot_dsds); if (ha->req_q_cnt < (req_cnt + 2)) { cnt = RD_REG_WORD_RELAXED(ISP_REQ_Q_OUT(ha, reg)); if (ha->req_ring_index < cnt) @@ -364,7 +364,7 @@ qla2x00_start_scsi(srb_t *sp) cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); /* Build IOCB segments */ - ha->isp_ops.build_iocbs(sp, cmd_pkt, tot_dsds); + ha->isp_ops->build_iocbs(sp, cmd_pkt, tot_dsds); /* Set total data segment count. */ cmd_pkt->entry_count = (uint8_t)req_cnt; @@ -432,7 +432,7 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, mrk->entry_type = MARKER_TYPE; mrk->modifier = type; if (type != MK_SYNC_ALL) { - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { mrk24 = (struct mrk_entry_24xx *) mrk; mrk24->nport_handle = cpu_to_le16(loop_id); mrk24->lun[1] = LSB(lun); @@ -487,7 +487,7 @@ qla2x00_req_pkt(scsi_qla_host_t *ha) for (timer = HZ; timer; timer--) { if ((req_cnt + 2) >= ha->req_q_cnt) { /* Calculate number of free request entries. */ - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) cnt = (uint16_t)RD_REG_DWORD( ®->isp24.req_q_out); else @@ -561,7 +561,7 @@ qla2x00_isp_cmd(scsi_qla_host_t *ha) ha->request_ring_ptr++; /* Set chip new ring index. */ - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { WRT_REG_DWORD(®->isp24.req_q_in, ha->req_ring_index); RD_REG_DWORD_RELAXED(®->isp24.req_q_in); } else { diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 0ba4c8d3787..b8f226ae263 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -143,7 +143,7 @@ qla2300_intr_handler(int irq, void *dev_id) WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); RD_REG_WORD(®->hccr); - ha->isp_ops.fw_dump(ha, 1); + ha->isp_ops->fw_dump(ha, 1); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; } else if ((stat & HSR_RISC_INT) == 0) @@ -247,7 +247,7 @@ void qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) { #define LS_UNKNOWN 2 - static char *link_speeds[5] = { "1", "2", "?", "4", "10" }; + static char *link_speeds[5] = { "1", "2", "?", "4", "8" }; char *link_speed; uint16_t handle_cnt; uint16_t cnt; @@ -334,9 +334,9 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n", mb[1], mb[2], mb[3]); - ha->isp_ops.fw_dump(ha, 1); + ha->isp_ops->fw_dump(ha, 1); - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { if (mb[1] == 0 && mb[2] == 0) { qla_printk(KERN_ERR, ha, "Unrecoverable Hardware Error: adapter " @@ -601,7 +601,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) "scsi(%ld): [R|Z]IO update completion.\n", ha->host_no)); - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) qla24xx_process_response_queue(ha); else qla2x00_process_response_queue(ha); @@ -823,7 +823,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) sts = (sts_entry_t *) pkt; sts24 = (struct sts_entry_24xx *) pkt; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { comp_status = le16_to_cpu(sts24->comp_status); scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK; } else { @@ -872,7 +872,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) fcport = sp->fcport; sense_len = rsp_info_len = resid_len = fw_resid_len = 0; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { sense_len = le32_to_cpu(sts24->sense_len); rsp_info_len = le32_to_cpu(sts24->rsp_data_len); resid_len = le32_to_cpu(sts24->rsp_residual_count); @@ -891,7 +891,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) /* Check for any FCP transport errors. */ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) { /* Sense data lies beyond any FCP RESPONSE data. */ - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) sense_data += rsp_info_len; if (rsp_info_len > 3 && rsp_info[3]) { DEBUG2(printk("scsi(%ld:%d:%d:%d) FCP I/O protocol " @@ -990,7 +990,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) case CS_DATA_UNDERRUN: resid = resid_len; /* Use F/W calculated residual length. */ - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) resid = fw_resid_len; if (scsi_status & SS_RESIDUAL_UNDER) { @@ -1062,6 +1062,25 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) cp->device->id, cp->device->lun, cp, cp->serial_number)); + /* + * In case of a Underrun condition, set both the lscsi + * status and the completion status to appropriate + * values. + */ + if (resid && + ((unsigned)(cp->request_bufflen - resid) < + cp->underflow)) { + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld:%d:%d:%d): Mid-layer underflow " + "detected (%x of %x bytes)...returning " + "error status.\n", ha->host_no, + cp->device->channel, cp->device->id, + cp->device->lun, resid, + cp->request_bufflen)); + + cp->result = DID_ERROR << 16 | lscsi_status; + } + if (sense_len) DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, CMD_ACTUAL_SNSLEN(cp))); @@ -1166,7 +1185,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) case CS_TIMEOUT: cp->result = DID_BUS_BUSY << 16; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d:%d): TIMEOUT status detected " "0x%x-0x%x\n", ha->host_no, cp->device->channel, @@ -1235,7 +1254,7 @@ qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt) } /* Move sense data. */ - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) host_to_fcp_swap(pkt->data, sizeof(pkt->data)); memcpy(sp->request_sense_ptr, pkt->data, sense_sz); DEBUG5(qla2x00_dump_buffer(sp->request_sense_ptr, sense_sz)); @@ -1483,7 +1502,7 @@ qla24xx_intr_handler(int irq, void *dev_id) qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " "Dumping firmware!\n", hccr); - ha->isp_ops.fw_dump(ha, 1); + ha->isp_ops->fw_dump(ha, 1); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; } else if ((stat & HSRX_RISC_INT) == 0) @@ -1617,7 +1636,7 @@ qla24xx_msix_default(int irq, void *dev_id) qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " "Dumping firmware!\n", hccr); - ha->isp_ops.fw_dump(ha, 1); + ha->isp_ops->fw_dump(ha, 1); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; } else if ((stat & HSRX_RISC_INT) == 0) @@ -1739,11 +1758,11 @@ qla2x00_request_irqs(scsi_qla_host_t *ha) int ret; /* If possible, enable MSI-X. */ - if (!IS_QLA2432(ha)) + if (!IS_QLA2432(ha) && !IS_QLA2532(ha)) goto skip_msix; - if (ha->chip_revision < QLA_MSIX_CHIP_REV_24XX || - !QLA_MSIX_FW_MODE_1(ha->fw_attributes)) { + if (IS_QLA2432(ha) && (ha->chip_revision < QLA_MSIX_CHIP_REV_24XX || + !QLA_MSIX_FW_MODE_1(ha->fw_attributes))) { DEBUG2(qla_printk(KERN_WARNING, ha, "MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n", ha->chip_revision, ha->fw_attributes)); @@ -1762,7 +1781,7 @@ qla2x00_request_irqs(scsi_qla_host_t *ha) "MSI-X: Falling back-to INTa mode -- %d.\n", ret); skip_msix: - if (!IS_QLA24XX(ha)) + if (!IS_QLA24XX(ha) && !IS_QLA2532(ha)) goto skip_msi; ret = pci_enable_msi(ha->pdev); @@ -1772,7 +1791,7 @@ skip_msix: } skip_msi: - ret = request_irq(ha->pdev->irq, ha->isp_ops.intr_handler, + ret = request_irq(ha->pdev->irq, ha->isp_ops->intr_handler, IRQF_DISABLED|IRQF_SHARED, QLA2XXX_DRIVER_NAME, ha); if (!ret) { ha->flags.inta_enabled = 1; diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 2cd0cff2592..d3746ec80a8 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -90,7 +90,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) spin_lock_irqsave(&ha->hardware_lock, flags); /* Load mailbox registers. */ - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) optr = (uint16_t __iomem *)®->isp24.mailbox0; else optr = (uint16_t __iomem *)MAILBOX_REG(ha, ®->isp, 0); @@ -154,7 +154,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) set_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags); - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) WRT_REG_DWORD(®->isp24.hccr, HCCRX_SET_HOST_INT); else WRT_REG_WORD(®->isp.hccr, HCCR_SET_HOST_INT); @@ -175,7 +175,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) DEBUG3_11(printk("%s(%ld): cmd=%x POLLING MODE.\n", __func__, ha->host_no, command)); - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) WRT_REG_DWORD(®->isp24.hccr, HCCRX_SET_HOST_INT); else WRT_REG_WORD(®->isp.hccr, HCCR_SET_HOST_INT); @@ -228,7 +228,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) uint16_t mb0; uint32_t ictrl; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { mb0 = RD_REG_WORD(®->isp24.mailbox0); ictrl = RD_REG_DWORD(®->isp24.ictrl); } else { @@ -322,7 +322,7 @@ qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t risc_addr, DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); - if (MSW(risc_addr) || IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (MSW(risc_addr) || IS_FWI2_CAPABLE(ha)) { mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED; mcp->mb[8] = MSW(risc_addr); mcp->out_mb = MBX_8|MBX_0; @@ -336,7 +336,7 @@ qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t risc_addr, mcp->mb[6] = MSW(MSD(req_dma)); mcp->mb[7] = LSW(MSD(req_dma)); mcp->out_mb |= MBX_7|MBX_6|MBX_3|MBX_2|MBX_1; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { mcp->mb[4] = MSW(risc_code_size); mcp->mb[5] = LSW(risc_code_size); mcp->out_mb |= MBX_5|MBX_4; @@ -387,7 +387,7 @@ qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr) mcp->mb[0] = MBC_EXECUTE_FIRMWARE; mcp->out_mb = MBX_0; mcp->in_mb = MBX_0; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { mcp->mb[1] = MSW(risc_addr); mcp->mb[2] = LSW(risc_addr); mcp->mb[3] = 0; @@ -410,7 +410,7 @@ qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr) DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__, ha->host_no, rval, mcp->mb[0])); } else { - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { DEBUG11(printk("%s(%ld): done exchanges=%x.\n", __func__, ha->host_no, mcp->mb[1])); } else { @@ -551,7 +551,7 @@ qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts) mcp->mb[3] = fwopts[3]; mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; mcp->in_mb = MBX_0; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { mcp->in_mb |= MBX_1; } else { mcp->mb[10] = fwopts[10]; @@ -664,7 +664,7 @@ qla2x00_verify_checksum(scsi_qla_host_t *ha, uint32_t risc_addr) mcp->mb[0] = MBC_VERIFY_CHECKSUM; mcp->out_mb = MBX_0; mcp->in_mb = MBX_0; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { mcp->mb[1] = MSW(risc_addr); mcp->mb[2] = LSW(risc_addr); mcp->out_mb |= MBX_2|MBX_1; @@ -681,8 +681,8 @@ qla2x00_verify_checksum(scsi_qla_host_t *ha, uint32_t risc_addr) if (rval != QLA_SUCCESS) { DEBUG2_3_11(printk("%s(%ld): failed=%x chk sum=%x.\n", __func__, - ha->host_no, rval, (IS_QLA24XX(ha) || IS_QLA54XX(ha) ? - (mcp->mb[2] << 16) | mcp->mb[1]: mcp->mb[1]))); + ha->host_no, rval, IS_FWI2_CAPABLE(ha) ? + (mcp->mb[2] << 16) | mcp->mb[1]: mcp->mb[1])); } else { DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); } @@ -739,7 +739,7 @@ qla2x00_issue_iocb(scsi_qla_host_t *ha, void* buffer, dma_addr_t phys_addr, /* Mask reserved bits. */ sts_entry->entry_status &= - IS_QLA24XX(ha) || IS_QLA54XX(ha) ? RF_MASK_24XX :RF_MASK; + IS_FWI2_CAPABLE(ha) ? RF_MASK_24XX :RF_MASK; } return rval; @@ -1085,7 +1085,7 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) memset(pd, 0, max(PORT_DATABASE_SIZE, PORT_DATABASE_24XX_SIZE)); mcp->mb[0] = MBC_GET_PORT_DATABASE; - if (opt != 0 && !IS_QLA24XX(ha) && !IS_QLA54XX(ha)) + if (opt != 0 && !IS_FWI2_CAPABLE(ha)) mcp->mb[0] = MBC_ENHANCED_GET_PORT_DATABASE; mcp->mb[2] = MSW(pd_dma); mcp->mb[3] = LSW(pd_dma); @@ -1094,7 +1094,7 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) mcp->mb[9] = ha->vp_idx; mcp->out_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; mcp->in_mb = MBX_0; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { mcp->mb[1] = fcport->loop_id; mcp->mb[10] = opt; mcp->out_mb |= MBX_10|MBX_1; @@ -1107,15 +1107,15 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) mcp->mb[1] = fcport->loop_id << 8 | opt; mcp->out_mb |= MBX_1; } - mcp->buf_size = (IS_QLA24XX(ha) || IS_QLA54XX(ha) ? - PORT_DATABASE_24XX_SIZE : PORT_DATABASE_SIZE); + mcp->buf_size = IS_FWI2_CAPABLE(ha) ? + PORT_DATABASE_24XX_SIZE : PORT_DATABASE_SIZE; mcp->flags = MBX_DMA_IN; mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2); rval = qla2x00_mailbox_command(ha, mcp); if (rval != QLA_SUCCESS) goto gpd_error_out; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { pd24 = (struct port_database_24xx *) pd; /* Check for logged in state. */ @@ -1333,7 +1333,7 @@ qla2x00_lip_reset(scsi_qla_host_t *ha) DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { mcp->mb[0] = MBC_LIP_FULL_LOGIN; mcp->mb[1] = BIT_6; mcp->mb[2] = 0; @@ -1637,7 +1637,7 @@ qla2x00_login_local_device(scsi_qla_host_t *ha, fc_port_t *fcport, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) + if (IS_FWI2_CAPABLE(ha)) return qla24xx_login_fabric(ha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa, mb_ret, opt); @@ -1821,7 +1821,7 @@ qla2x00_full_login_lip(scsi_qla_host_t *ha) ha->host_no)); mcp->mb[0] = MBC_LIP_FULL_LOGIN; - mcp->mb[1] = IS_QLA24XX(ha) || IS_QLA54XX(ha) ? BIT_3: 0; + mcp->mb[1] = IS_FWI2_CAPABLE(ha) ? BIT_3: 0; mcp->mb[2] = 0; mcp->mb[3] = 0; mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; @@ -1871,7 +1871,7 @@ qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma, mcp->mb[0] = MBC_GET_ID_LIST; mcp->out_mb = MBX_0; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { mcp->mb[2] = MSW(id_list_dma); mcp->mb[3] = LSW(id_list_dma); mcp->mb[6] = MSW(MSD(id_list_dma)); @@ -2063,7 +2063,7 @@ qla2x00_get_link_status(scsi_qla_host_t *ha, uint16_t loop_id, mcp->mb[7] = LSW(MSD(stat_buf_dma)); mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; mcp->in_mb = MBX_0; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { mcp->mb[1] = loop_id; mcp->mb[4] = 0; mcp->mb[10] = 0; @@ -2334,7 +2334,7 @@ qla2x00_system_error(scsi_qla_host_t *ha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA24XX(ha) && !IS_QLA54XX(ha)) + if (!IS_FWI2_CAPABLE(ha)) return QLA_FUNCTION_FAILED; DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); @@ -2444,7 +2444,7 @@ qla2x00_stop_firmware(scsi_qla_host_t *ha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA24XX(ha) && !IS_QLA54XX(ha)) + if (!IS_FWI2_CAPABLE(ha)) return QLA_FUNCTION_FAILED; DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); @@ -2474,7 +2474,7 @@ qla2x00_trace_control(scsi_qla_host_t *ha, uint16_t ctrl, dma_addr_t eft_dma, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA24XX(ha) && !IS_QLA54XX(ha)) + if (!IS_FWI2_CAPABLE(ha)) return QLA_FUNCTION_FAILED; DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); @@ -2514,7 +2514,7 @@ qla2x00_read_sfp(scsi_qla_host_t *ha, dma_addr_t sfp_dma, uint16_t addr, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA24XX(ha) && !IS_QLA54XX(ha)) + if (!IS_FWI2_CAPABLE(ha)) return QLA_FUNCTION_FAILED; DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); @@ -2552,7 +2552,7 @@ qla2x00_get_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA24XX(ha)) + if (!IS_IIDMA_CAPABLE(ha)) return QLA_FUNCTION_FAILED; DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); @@ -2595,7 +2595,7 @@ qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA24XX(ha)) + if (!IS_IIDMA_CAPABLE(ha)) return QLA_FUNCTION_FAILED; DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 92376f9dfdd..c488996cb95 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -265,6 +265,8 @@ qla24xx_pci_info_str(struct scsi_qla_host *ha, char *str) strcpy(str, "PCIe ("); if (lspeed == 1) strcat(str, "2.5Gb/s "); + else if (lspeed == 2) + strcat(str, "5.0Gb/s "); else strcat(str, "<unknown> "); snprintf(lwstr, sizeof(lwstr), "x%d)", lwidth); @@ -343,6 +345,12 @@ qla24xx_fw_version_str(struct scsi_qla_host *ha, char *str) strcat(str, "[IP] "); if (ha->fw_attributes & BIT_2) strcat(str, "[Multi-ID] "); + if (ha->fw_attributes & BIT_3) + strcat(str, "[SB-2] "); + if (ha->fw_attributes & BIT_4) + strcat(str, "[T10 CRC] "); + if (ha->fw_attributes & BIT_5) + strcat(str, "[VI] "); if (ha->fw_attributes & BIT_13) strcat(str, "[Experimental]"); return str; @@ -681,7 +689,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) DEBUG3(qla2x00_print_scsi_cmd(cmd)); spin_unlock_irqrestore(&pha->hardware_lock, flags); - if (ha->isp_ops.abort_command(ha, sp)) { + if (ha->isp_ops->abort_command(ha, sp)) { DEBUG2(printk("%s(%ld): abort_command " "mbx failed.\n", __func__, ha->host_no)); } else { @@ -813,7 +821,7 @@ qla2xxx_eh_device_reset(struct scsi_cmnd *cmd) #if defined(LOGOUT_AFTER_DEVICE_RESET) if (ret == SUCCESS) { if (fcport->flags & FC_FABRIC_DEVICE) { - ha->isp_ops.fabric_logout(ha, fcport->loop_id); + ha->isp_ops->fabric_logout(ha, fcport->loop_id); qla2x00_mark_device_lost(ha, fcport, 0, 0); } } @@ -1105,7 +1113,7 @@ static int qla2x00_device_reset(scsi_qla_host_t *ha, fc_port_t *reset_fcport) { /* Abort Target command will clear Reservation */ - return ha->isp_ops.abort_target(reset_fcport); + return ha->isp_ops->abort_target(reset_fcport); } static int @@ -1184,8 +1192,8 @@ qla2x00_config_dma_addressing(scsi_qla_host_t *ha) !pci_set_consistent_dma_mask(ha->pdev, DMA_64BIT_MASK)) { /* Ok, a 64bit DMA mask is applicable. */ ha->flags.enable_64bit_addressing = 1; - ha->isp_ops.calc_req_entries = qla2x00_calc_iocbs_64; - ha->isp_ops.build_iocbs = qla2x00_build_scsi_iocbs_64; + ha->isp_ops->calc_req_entries = qla2x00_calc_iocbs_64; + ha->isp_ops->build_iocbs = qla2x00_build_scsi_iocbs_64; return; } } @@ -1194,6 +1202,193 @@ qla2x00_config_dma_addressing(scsi_qla_host_t *ha) pci_set_consistent_dma_mask(ha->pdev, DMA_32BIT_MASK); } +static void +qla2x00_enable_intrs(scsi_qla_host_t *ha) +{ + unsigned long flags = 0; + struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + + spin_lock_irqsave(&ha->hardware_lock, flags); + ha->interrupts_on = 1; + /* enable risc and host interrupts */ + WRT_REG_WORD(®->ictrl, ICR_EN_INT | ICR_EN_RISC); + RD_REG_WORD(®->ictrl); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + +} + +static void +qla2x00_disable_intrs(scsi_qla_host_t *ha) +{ + unsigned long flags = 0; + struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + + spin_lock_irqsave(&ha->hardware_lock, flags); + ha->interrupts_on = 0; + /* disable risc and host interrupts */ + WRT_REG_WORD(®->ictrl, 0); + RD_REG_WORD(®->ictrl); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +static void +qla24xx_enable_intrs(scsi_qla_host_t *ha) +{ + unsigned long flags = 0; + struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + + spin_lock_irqsave(&ha->hardware_lock, flags); + ha->interrupts_on = 1; + WRT_REG_DWORD(®->ictrl, ICRX_EN_RISC_INT); + RD_REG_DWORD(®->ictrl); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +static void +qla24xx_disable_intrs(scsi_qla_host_t *ha) +{ + unsigned long flags = 0; + struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + + spin_lock_irqsave(&ha->hardware_lock, flags); + ha->interrupts_on = 0; + WRT_REG_DWORD(®->ictrl, 0); + RD_REG_DWORD(®->ictrl); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +static struct isp_operations qla2100_isp_ops = { + .pci_config = qla2100_pci_config, + .reset_chip = qla2x00_reset_chip, + .chip_diag = qla2x00_chip_diag, + .config_rings = qla2x00_config_rings, + .reset_adapter = qla2x00_reset_adapter, + .nvram_config = qla2x00_nvram_config, + .update_fw_options = qla2x00_update_fw_options, + .load_risc = qla2x00_load_risc, + .pci_info_str = qla2x00_pci_info_str, + .fw_version_str = qla2x00_fw_version_str, + .intr_handler = qla2100_intr_handler, + .enable_intrs = qla2x00_enable_intrs, + .disable_intrs = qla2x00_disable_intrs, + .abort_command = qla2x00_abort_command, + .abort_target = qla2x00_abort_target, + .fabric_login = qla2x00_login_fabric, + .fabric_logout = qla2x00_fabric_logout, + .calc_req_entries = qla2x00_calc_iocbs_32, + .build_iocbs = qla2x00_build_scsi_iocbs_32, + .prep_ms_iocb = qla2x00_prep_ms_iocb, + .prep_ms_fdmi_iocb = qla2x00_prep_ms_fdmi_iocb, + .read_nvram = qla2x00_read_nvram_data, + .write_nvram = qla2x00_write_nvram_data, + .fw_dump = qla2100_fw_dump, + .beacon_on = NULL, + .beacon_off = NULL, + .beacon_blink = NULL, + .read_optrom = qla2x00_read_optrom_data, + .write_optrom = qla2x00_write_optrom_data, + .get_flash_version = qla2x00_get_flash_version, +}; + +static struct isp_operations qla2300_isp_ops = { + .pci_config = qla2300_pci_config, + .reset_chip = qla2x00_reset_chip, + .chip_diag = qla2x00_chip_diag, + .config_rings = qla2x00_config_rings, + .reset_adapter = qla2x00_reset_adapter, + .nvram_config = qla2x00_nvram_config, + .update_fw_options = qla2x00_update_fw_options, + .load_risc = qla2x00_load_risc, + .pci_info_str = qla2x00_pci_info_str, + .fw_version_str = qla2x00_fw_version_str, + .intr_handler = qla2300_intr_handler, + .enable_intrs = qla2x00_enable_intrs, + .disable_intrs = qla2x00_disable_intrs, + .abort_command = qla2x00_abort_command, + .abort_target = qla2x00_abort_target, + .fabric_login = qla2x00_login_fabric, + .fabric_logout = qla2x00_fabric_logout, + .calc_req_entries = qla2x00_calc_iocbs_32, + .build_iocbs = qla2x00_build_scsi_iocbs_32, + .prep_ms_iocb = qla2x00_prep_ms_iocb, + .prep_ms_fdmi_iocb = qla2x00_prep_ms_fdmi_iocb, + .read_nvram = qla2x00_read_nvram_data, + .write_nvram = qla2x00_write_nvram_data, + .fw_dump = qla2300_fw_dump, + .beacon_on = qla2x00_beacon_on, + .beacon_off = qla2x00_beacon_off, + .beacon_blink = qla2x00_beacon_blink, + .read_optrom = qla2x00_read_optrom_data, + .write_optrom = qla2x00_write_optrom_data, + .get_flash_version = qla2x00_get_flash_version, +}; + +static struct isp_operations qla24xx_isp_ops = { + .pci_config = qla24xx_pci_config, + .reset_chip = qla24xx_reset_chip, + .chip_diag = qla24xx_chip_diag, + .config_rings = qla24xx_config_rings, + .reset_adapter = qla24xx_reset_adapter, + .nvram_config = qla24xx_nvram_config, + .update_fw_options = qla24xx_update_fw_options, + .load_risc = qla24xx_load_risc, + .pci_info_str = qla24xx_pci_info_str, + .fw_version_str = qla24xx_fw_version_str, + .intr_handler = qla24xx_intr_handler, + .enable_intrs = qla24xx_enable_intrs, + .disable_intrs = qla24xx_disable_intrs, + .abort_command = qla24xx_abort_command, + .abort_target = qla24xx_abort_target, + .fabric_login = qla24xx_login_fabric, + .fabric_logout = qla24xx_fabric_logout, + .calc_req_entries = NULL, + .build_iocbs = NULL, + .prep_ms_iocb = qla24xx_prep_ms_iocb, + .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb, + .read_nvram = qla24xx_read_nvram_data, + .write_nvram = qla24xx_write_nvram_data, + .fw_dump = qla24xx_fw_dump, + .beacon_on = qla24xx_beacon_on, + .beacon_off = qla24xx_beacon_off, + .beacon_blink = qla24xx_beacon_blink, + .read_optrom = qla24xx_read_optrom_data, + .write_optrom = qla24xx_write_optrom_data, + .get_flash_version = qla24xx_get_flash_version, +}; + +static struct isp_operations qla25xx_isp_ops = { + .pci_config = qla25xx_pci_config, + .reset_chip = qla24xx_reset_chip, + .chip_diag = qla24xx_chip_diag, + .config_rings = qla24xx_config_rings, + .reset_adapter = qla24xx_reset_adapter, + .nvram_config = qla24xx_nvram_config, + .update_fw_options = qla24xx_update_fw_options, + .load_risc = qla24xx_load_risc, + .pci_info_str = qla24xx_pci_info_str, + .fw_version_str = qla24xx_fw_version_str, + .intr_handler = qla24xx_intr_handler, + .enable_intrs = qla24xx_enable_intrs, + .disable_intrs = qla24xx_disable_intrs, + .abort_command = qla24xx_abort_command, + .abort_target = qla24xx_abort_target, + .fabric_login = qla24xx_login_fabric, + .fabric_logout = qla24xx_fabric_logout, + .calc_req_entries = NULL, + .build_iocbs = NULL, + .prep_ms_iocb = qla24xx_prep_ms_iocb, + .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb, + .read_nvram = qla25xx_read_nvram_data, + .write_nvram = qla25xx_write_nvram_data, + .fw_dump = qla25xx_fw_dump, + .beacon_on = qla24xx_beacon_on, + .beacon_off = qla24xx_beacon_off, + .beacon_blink = qla24xx_beacon_blink, + .read_optrom = qla24xx_read_optrom_data, + .write_optrom = qla24xx_write_optrom_data, + .get_flash_version = qla24xx_get_flash_version, +}; + static inline void qla2x00_set_isp_flags(scsi_qla_host_t *ha) { @@ -1238,19 +1433,32 @@ qla2x00_set_isp_flags(scsi_qla_host_t *ha) case PCI_DEVICE_ID_QLOGIC_ISP2422: ha->device_type |= DT_ISP2422; ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->device_type |= DT_IIDMA; ha->fw_srisc_address = RISC_START_ADDRESS_2400; break; case PCI_DEVICE_ID_QLOGIC_ISP2432: ha->device_type |= DT_ISP2432; ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->device_type |= DT_IIDMA; ha->fw_srisc_address = RISC_START_ADDRESS_2400; break; case PCI_DEVICE_ID_QLOGIC_ISP5422: ha->device_type |= DT_ISP5422; + ha->device_type |= DT_FWI2; ha->fw_srisc_address = RISC_START_ADDRESS_2400; break; case PCI_DEVICE_ID_QLOGIC_ISP5432: ha->device_type |= DT_ISP5432; + ha->device_type |= DT_FWI2; + ha->fw_srisc_address = RISC_START_ADDRESS_2400; + break; + case PCI_DEVICE_ID_QLOGIC_ISP2532: + ha->device_type |= DT_ISP2532; + ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->device_type |= DT_IIDMA; ha->fw_srisc_address = RISC_START_ADDRESS_2400; break; } @@ -1323,61 +1531,6 @@ iospace_error_exit: } static void -qla2x00_enable_intrs(scsi_qla_host_t *ha) -{ - unsigned long flags = 0; - struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - - spin_lock_irqsave(&ha->hardware_lock, flags); - ha->interrupts_on = 1; - /* enable risc and host interrupts */ - WRT_REG_WORD(®->ictrl, ICR_EN_INT | ICR_EN_RISC); - RD_REG_WORD(®->ictrl); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - -} - -static void -qla2x00_disable_intrs(scsi_qla_host_t *ha) -{ - unsigned long flags = 0; - struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - - spin_lock_irqsave(&ha->hardware_lock, flags); - ha->interrupts_on = 0; - /* disable risc and host interrupts */ - WRT_REG_WORD(®->ictrl, 0); - RD_REG_WORD(®->ictrl); - spin_unlock_irqrestore(&ha->hardware_lock, flags); -} - -static void -qla24xx_enable_intrs(scsi_qla_host_t *ha) -{ - unsigned long flags = 0; - struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - - spin_lock_irqsave(&ha->hardware_lock, flags); - ha->interrupts_on = 1; - WRT_REG_DWORD(®->ictrl, ICRX_EN_RISC_INT); - RD_REG_DWORD(®->ictrl); - spin_unlock_irqrestore(&ha->hardware_lock, flags); -} - -static void -qla24xx_disable_intrs(scsi_qla_host_t *ha) -{ - unsigned long flags = 0; - struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - - spin_lock_irqsave(&ha->hardware_lock, flags); - ha->interrupts_on = 0; - WRT_REG_DWORD(®->ictrl, 0); - RD_REG_DWORD(®->ictrl); - spin_unlock_irqrestore(&ha->hardware_lock, flags); -} - -static void qla2xxx_scan_start(struct Scsi_Host *shost) { scsi_qla_host_t *ha = (scsi_qla_host_t *)shost->hostdata; @@ -1422,7 +1575,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5422 || - pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432) + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532) sht = &qla24xx_driver_template; host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t)); if (host == NULL) { @@ -1466,33 +1620,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->max_q_depth = ql2xmaxqdepth; /* Assign ISP specific operations. */ - ha->isp_ops.pci_config = qla2100_pci_config; - ha->isp_ops.reset_chip = qla2x00_reset_chip; - ha->isp_ops.chip_diag = qla2x00_chip_diag; - ha->isp_ops.config_rings = qla2x00_config_rings; - ha->isp_ops.reset_adapter = qla2x00_reset_adapter; - ha->isp_ops.nvram_config = qla2x00_nvram_config; - ha->isp_ops.update_fw_options = qla2x00_update_fw_options; - ha->isp_ops.load_risc = qla2x00_load_risc; - ha->isp_ops.pci_info_str = qla2x00_pci_info_str; - ha->isp_ops.fw_version_str = qla2x00_fw_version_str; - ha->isp_ops.intr_handler = qla2100_intr_handler; - ha->isp_ops.enable_intrs = qla2x00_enable_intrs; - ha->isp_ops.disable_intrs = qla2x00_disable_intrs; - ha->isp_ops.abort_command = qla2x00_abort_command; - ha->isp_ops.abort_target = qla2x00_abort_target; - ha->isp_ops.fabric_login = qla2x00_login_fabric; - ha->isp_ops.fabric_logout = qla2x00_fabric_logout; - ha->isp_ops.calc_req_entries = qla2x00_calc_iocbs_32; - ha->isp_ops.build_iocbs = qla2x00_build_scsi_iocbs_32; - ha->isp_ops.prep_ms_iocb = qla2x00_prep_ms_iocb; - ha->isp_ops.prep_ms_fdmi_iocb = qla2x00_prep_ms_fdmi_iocb; - ha->isp_ops.read_nvram = qla2x00_read_nvram_data; - ha->isp_ops.write_nvram = qla2x00_write_nvram_data; - ha->isp_ops.fw_dump = qla2100_fw_dump; - ha->isp_ops.read_optrom = qla2x00_read_optrom_data; - ha->isp_ops.write_optrom = qla2x00_write_optrom_data; - ha->isp_ops.get_flash_version = qla2x00_get_flash_version; if (IS_QLA2100(ha)) { host->max_id = MAX_TARGETS_2100; ha->mbx_count = MAILBOX_REGISTER_COUNT_2100; @@ -1501,6 +1628,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->last_loop_id = SNS_LAST_LOOP_ID_2100; host->sg_tablesize = 32; ha->gid_list_info_size = 4; + ha->isp_ops = &qla2100_isp_ops; } else if (IS_QLA2200(ha)) { host->max_id = MAX_TARGETS_2200; ha->mbx_count = MAILBOX_REGISTER_COUNT; @@ -1508,21 +1636,17 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->response_q_length = RESPONSE_ENTRY_CNT_2100; ha->last_loop_id = SNS_LAST_LOOP_ID_2100; ha->gid_list_info_size = 4; + ha->isp_ops = &qla2100_isp_ops; } else if (IS_QLA23XX(ha)) { host->max_id = MAX_TARGETS_2200; ha->mbx_count = MAILBOX_REGISTER_COUNT; ha->request_q_length = REQUEST_ENTRY_CNT_2200; ha->response_q_length = RESPONSE_ENTRY_CNT_2300; ha->last_loop_id = SNS_LAST_LOOP_ID_2300; - ha->isp_ops.pci_config = qla2300_pci_config; - ha->isp_ops.intr_handler = qla2300_intr_handler; - ha->isp_ops.fw_dump = qla2300_fw_dump; - ha->isp_ops.beacon_on = qla2x00_beacon_on; - ha->isp_ops.beacon_off = qla2x00_beacon_off; - ha->isp_ops.beacon_blink = qla2x00_beacon_blink; ha->gid_list_info_size = 6; if (IS_QLA2322(ha) || IS_QLA6322(ha)) ha->optrom_size = OPTROM_SIZE_2322; + ha->isp_ops = &qla2300_isp_ops; } else if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { host->max_id = MAX_TARGETS_2200; ha->mbx_count = MAILBOX_REGISTER_COUNT; @@ -1531,36 +1655,20 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->last_loop_id = SNS_LAST_LOOP_ID_2300; ha->init_cb_size = sizeof(struct mid_init_cb_24xx); ha->mgmt_svr_loop_id = 10 + ha->vp_idx; - ha->isp_ops.pci_config = qla24xx_pci_config; - ha->isp_ops.reset_chip = qla24xx_reset_chip; - ha->isp_ops.chip_diag = qla24xx_chip_diag; - ha->isp_ops.config_rings = qla24xx_config_rings; - ha->isp_ops.reset_adapter = qla24xx_reset_adapter; - ha->isp_ops.nvram_config = qla24xx_nvram_config; - ha->isp_ops.update_fw_options = qla24xx_update_fw_options; - ha->isp_ops.load_risc = qla24xx_load_risc; - ha->isp_ops.pci_info_str = qla24xx_pci_info_str; - ha->isp_ops.fw_version_str = qla24xx_fw_version_str; - ha->isp_ops.intr_handler = qla24xx_intr_handler; - ha->isp_ops.enable_intrs = qla24xx_enable_intrs; - ha->isp_ops.disable_intrs = qla24xx_disable_intrs; - ha->isp_ops.abort_command = qla24xx_abort_command; - ha->isp_ops.abort_target = qla24xx_abort_target; - ha->isp_ops.fabric_login = qla24xx_login_fabric; - ha->isp_ops.fabric_logout = qla24xx_fabric_logout; - ha->isp_ops.prep_ms_iocb = qla24xx_prep_ms_iocb; - ha->isp_ops.prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb; - ha->isp_ops.read_nvram = qla24xx_read_nvram_data; - ha->isp_ops.write_nvram = qla24xx_write_nvram_data; - ha->isp_ops.fw_dump = qla24xx_fw_dump; - ha->isp_ops.read_optrom = qla24xx_read_optrom_data; - ha->isp_ops.write_optrom = qla24xx_write_optrom_data; - ha->isp_ops.beacon_on = qla24xx_beacon_on; - ha->isp_ops.beacon_off = qla24xx_beacon_off; - ha->isp_ops.beacon_blink = qla24xx_beacon_blink; - ha->isp_ops.get_flash_version = qla24xx_get_flash_version; ha->gid_list_info_size = 8; ha->optrom_size = OPTROM_SIZE_24XX; + ha->isp_ops = &qla24xx_isp_ops; + } else if (IS_QLA25XX(ha)) { + host->max_id = MAX_TARGETS_2200; + ha->mbx_count = MAILBOX_REGISTER_COUNT; + ha->request_q_length = REQUEST_ENTRY_CNT_24XX; + ha->response_q_length = RESPONSE_ENTRY_CNT_2300; + ha->last_loop_id = SNS_LAST_LOOP_ID_2300; + ha->init_cb_size = sizeof(struct mid_init_cb_24xx); + ha->mgmt_svr_loop_id = 10 + ha->vp_idx; + ha->gid_list_info_size = 8; + ha->optrom_size = OPTROM_SIZE_25XX; + ha->isp_ops = &qla25xx_isp_ops; } host->can_queue = ha->request_q_length + 128; @@ -1628,11 +1736,11 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n", ha->host_no, ha)); - ha->isp_ops.disable_intrs(ha); + ha->isp_ops->disable_intrs(ha); spin_lock_irqsave(&ha->hardware_lock, flags); reg = ha->iobase; - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { WRT_REG_DWORD(®->isp24.hccr, HCCRX_CLR_HOST_INT); WRT_REG_DWORD(®->isp24.hccr, HCCRX_CLR_RISC_INT); } else { @@ -1654,7 +1762,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) } spin_unlock_irqrestore(&ha->hardware_lock, flags); - ha->isp_ops.enable_intrs(ha); + ha->isp_ops->enable_intrs(ha); pci_set_drvdata(pdev, ha); @@ -1679,9 +1787,9 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) " ISP%04X: %s @ %s hdma%c, host#=%ld, fw=%s\n", qla2x00_version_str, ha->model_number, ha->model_desc ? ha->model_desc: "", pdev->device, - ha->isp_ops.pci_info_str(ha, pci_info), pci_name(pdev), + ha->isp_ops->pci_info_str(ha, pci_info), pci_name(pdev), ha->flags.enable_64bit_addressing ? '+': '-', ha->host_no, - ha->isp_ops.fw_version_str(ha, fw_str)); + ha->isp_ops->fw_version_str(ha, fw_str)); return 0; @@ -1747,7 +1855,7 @@ qla2x00_free_device(scsi_qla_host_t *ha) /* turn-off interrupts on the card */ if (ha->interrupts_on) - ha->isp_ops.disable_intrs(ha); + ha->isp_ops->disable_intrs(ha); qla2x00_mem_free(ha); @@ -2025,7 +2133,7 @@ qla2x00_mem_alloc(scsi_qla_host_t *ha) } memset(ha->ct_sns, 0, sizeof(struct ct_sns_pkt)); - if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { + if (IS_FWI2_CAPABLE(ha)) { /* * Get consistent memory allocated for SFP * block. @@ -2305,7 +2413,7 @@ qla2x00_do_dpc(void *data) if (fcport->flags & FCF_FABRIC_DEVICE) { if (fcport->flags & FCF_TAPE_PRESENT) - ha->isp_ops.fabric_logout( + ha->isp_ops->fabric_logout( ha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, @@ -2385,10 +2493,10 @@ qla2x00_do_dpc(void *data) } if (!ha->interrupts_on) - ha->isp_ops.enable_intrs(ha); + ha->isp_ops->enable_intrs(ha); if (test_and_clear_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags)) - ha->isp_ops.beacon_blink(ha); + ha->isp_ops->beacon_blink(ha); qla2x00_do_dpc_all_vps(ha); @@ -2617,18 +2725,20 @@ qla2x00_down_timeout(struct semaphore *sema, unsigned long timeout) /* Firmware interface routines. */ -#define FW_BLOBS 5 +#define FW_BLOBS 6 #define FW_ISP21XX 0 #define FW_ISP22XX 1 #define FW_ISP2300 2 #define FW_ISP2322 3 #define FW_ISP24XX 4 +#define FW_ISP25XX 5 #define FW_FILE_ISP21XX "ql2100_fw.bin" #define FW_FILE_ISP22XX "ql2200_fw.bin" #define FW_FILE_ISP2300 "ql2300_fw.bin" #define FW_FILE_ISP2322 "ql2322_fw.bin" #define FW_FILE_ISP24XX "ql2400_fw.bin" +#define FW_FILE_ISP25XX "ql2500_fw.bin" static DECLARE_MUTEX(qla_fw_lock); @@ -2638,6 +2748,7 @@ static struct fw_blob qla_fw_blobs[FW_BLOBS] = { { .name = FW_FILE_ISP2300, .segs = { 0x800, 0 }, }, { .name = FW_FILE_ISP2322, .segs = { 0x800, 0x1c000, 0x1e000, 0 }, }, { .name = FW_FILE_ISP24XX, }, + { .name = FW_FILE_ISP25XX, }, }; struct fw_blob * @@ -2656,6 +2767,8 @@ qla2x00_request_firmware(scsi_qla_host_t *ha) blob = &qla_fw_blobs[FW_ISP2322]; } else if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { blob = &qla_fw_blobs[FW_ISP24XX]; + } else if (IS_QLA25XX(ha)) { + blob = &qla_fw_blobs[FW_ISP25XX]; } down(&qla_fw_lock); @@ -2699,6 +2812,7 @@ static struct pci_device_id qla2xxx_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2432) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5422) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5432) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2532) }, { 0 }, }; MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl); diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 206bda093da..a925a3f179f 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -766,6 +766,29 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, return ret; } +uint8_t * +qla25xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, + uint32_t bytes) +{ + uint32_t i; + uint32_t *dwptr; + + /* Dword reads to flash. */ + dwptr = (uint32_t *)buf; + for (i = 0; i < bytes >> 2; i++, naddr++) + dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha, + flash_data_to_access_addr(FA_VPD_NVRAM_ADDR | naddr))); + + return buf; +} + +int +qla25xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, + uint32_t bytes) +{ + return qla24xx_write_flash_data(ha, (uint32_t *)buf, + FA_VPD_NVRAM_ADDR | naddr, bytes >> 2); +} static inline void qla2x00_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags) @@ -919,7 +942,7 @@ qla2x00_beacon_off(struct scsi_qla_host *ha) else ha->beacon_color_state = QLA_LED_GRN_ON; - ha->isp_ops.beacon_blink(ha); /* This turns green LED off */ + ha->isp_ops->beacon_blink(ha); /* This turns green LED off */ ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING; ha->fw_options[1] &= ~FO1_DISABLE_GPIO6_7; @@ -1031,7 +1054,7 @@ qla24xx_beacon_off(struct scsi_qla_host *ha) ha->beacon_blink_led = 0; ha->beacon_color_state = QLA_LED_ALL_ON; - ha->isp_ops.beacon_blink(ha); /* Will flip to all off. */ + ha->isp_ops->beacon_blink(ha); /* Will flip to all off. */ /* Give control back to firmware. */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -1419,7 +1442,7 @@ qla2x00_suspend_hba(struct scsi_qla_host *ha) /* Suspend HBA. */ scsi_block_requests(ha->host); - ha->isp_ops.disable_intrs(ha); + ha->isp_ops->disable_intrs(ha); set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); /* Pause RISC. */ @@ -1705,7 +1728,7 @@ qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, { /* Suspend HBA. */ scsi_block_requests(ha->host); - ha->isp_ops.disable_intrs(ha); + ha->isp_ops->disable_intrs(ha); set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); /* Go with read. */ @@ -1713,7 +1736,7 @@ qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, /* Resume HBA. */ clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); - ha->isp_ops.enable_intrs(ha); + ha->isp_ops->enable_intrs(ha); scsi_unblock_requests(ha->host); return buf; @@ -1727,7 +1750,7 @@ qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, /* Suspend HBA. */ scsi_block_requests(ha->host); - ha->isp_ops.disable_intrs(ha); + ha->isp_ops->disable_intrs(ha); set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); /* Go with write. */ diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index fd2f10a2534..dd1f8ceb79c 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.02.00-k1" +#define QLA2XXX_VERSION "8.02.00-k2" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 2 diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 4cd9c58efef..4947dfe625a 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -2875,7 +2875,7 @@ static int __init scsi_debug_init(void) init_all_queued(); - sdebug_driver_template.proc_name = (char *)sdebug_proc_name; + sdebug_driver_template.proc_name = sdebug_proc_name; host_to_add = scsi_debug_add_host; scsi_debug_add_host = 0; diff --git a/drivers/scsi/scsi_sysctl.c b/drivers/scsi/scsi_sysctl.c index 6cfaaa2d0c8..63a30f566f3 100644 --- a/drivers/scsi/scsi_sysctl.c +++ b/drivers/scsi/scsi_sysctl.c @@ -9,6 +9,7 @@ #include <linux/sysctl.h> #include "scsi_logging.h" +#include "scsi_priv.h" static ctl_table scsi_table[] = { diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index ed720863ab9..34cdce6738a 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -16,6 +16,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_transport.h> +#include <scsi/scsi_driver.h> #include "scsi_priv.h" #include "scsi_logging.h" @@ -714,6 +715,7 @@ static int attr_add(struct device *dev, struct device_attribute *attr) int scsi_sysfs_add_sdev(struct scsi_device *sdev) { int error, i; + struct request_queue *rq = sdev->request_queue; if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0) return error; @@ -733,6 +735,17 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) /* take a reference for the sdev_classdev; this is * released by the sdev_class .release */ get_device(&sdev->sdev_gendev); + + error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL); + + if (error) + sdev_printk(KERN_INFO, sdev, + "Failed to register bsg queue, errno=%d\n", error); + + /* we're treating error on bsg register as non-fatal, so pretend + * nothing went wrong */ + error = 0; + if (sdev->host->hostt->sdev_attrs) { for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) { error = attr_add(&sdev->sdev_gendev, @@ -779,6 +792,7 @@ void __scsi_remove_device(struct scsi_device *sdev) if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0) return; + bsg_unregister_queue(sdev->request_queue); class_device_unregister(&sdev->sdev_classdev); transport_remove_device(dev); device_del(dev); @@ -803,7 +817,7 @@ void scsi_remove_device(struct scsi_device *sdev) } EXPORT_SYMBOL(scsi_remove_device); -void __scsi_remove_target(struct scsi_target *starget) +static void __scsi_remove_target(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); unsigned long flags; diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index e8825709797..47057254850 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -2358,7 +2358,7 @@ fc_rport_final_delete(struct work_struct *work) * Notes: * This routine assumes no locks are held on entry. **/ -struct fc_rport * +static struct fc_rport * fc_rport_create(struct Scsi_Host *shost, int channel, struct fc_rport_identifiers *ids) { diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index b2ef71a8629..3120f4b3a11 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -29,6 +29,8 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/blkdev.h> +#include <linux/bsg.h> #include <scsi/scsi.h> #include <scsi/scsi_device.h> @@ -40,6 +42,7 @@ struct sas_host_attrs { struct list_head rphy_list; struct mutex lock; + struct request_queue *q; u32 next_target_id; u32 next_expander_id; int next_port_id; @@ -152,6 +155,106 @@ static struct { sas_bitfield_name_search(linkspeed, sas_linkspeed_names) sas_bitfield_name_set(linkspeed, sas_linkspeed_names) +static void sas_smp_request(struct request_queue *q, struct Scsi_Host *shost, + struct sas_rphy *rphy) +{ + struct request *req; + int ret; + int (*handler)(struct Scsi_Host *, struct sas_rphy *, struct request *); + + while (!blk_queue_plugged(q)) { + req = elv_next_request(q); + if (!req) + break; + + blkdev_dequeue_request(req); + + spin_unlock_irq(q->queue_lock); + + handler = to_sas_internal(shost->transportt)->f->smp_handler; + ret = handler(shost, rphy, req); + + spin_lock_irq(q->queue_lock); + + req->end_io(req, ret); + } +} + +static void sas_host_smp_request(struct request_queue *q) +{ + sas_smp_request(q, (struct Scsi_Host *)q->queuedata, NULL); +} + +static void sas_non_host_smp_request(struct request_queue *q) +{ + struct sas_rphy *rphy = q->queuedata; + sas_smp_request(q, rphy_to_shost(rphy), rphy); +} + +static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) +{ + struct request_queue *q; + int error; + struct device *dev; + char namebuf[BUS_ID_SIZE]; + const char *name; + + if (!to_sas_internal(shost->transportt)->f->smp_handler) { + printk("%s can't handle SMP requests\n", shost->hostt->name); + return 0; + } + + if (rphy) { + q = blk_init_queue(sas_non_host_smp_request, NULL); + dev = &rphy->dev; + name = dev->bus_id; + } else { + q = blk_init_queue(sas_host_smp_request, NULL); + dev = &shost->shost_gendev; + snprintf(namebuf, sizeof(namebuf), + "sas_host%d", shost->host_no); + name = namebuf; + } + if (!q) + return -ENOMEM; + + error = bsg_register_queue(q, dev, name); + if (error) { + blk_cleanup_queue(q); + return -ENOMEM; + } + + if (rphy) + rphy->q = q; + else + to_sas_host_attrs(shost)->q = q; + + if (rphy) + q->queuedata = rphy; + else + q->queuedata = shost; + + set_bit(QUEUE_FLAG_BIDI, &q->queue_flags); + + return 0; +} + +static void sas_bsg_remove(struct Scsi_Host *shost, struct sas_rphy *rphy) +{ + struct request_queue *q; + + if (rphy) + q = rphy->q; + else + q = to_sas_host_attrs(shost)->q; + + if (!q) + return; + + bsg_unregister_queue(q); + blk_cleanup_queue(q); +} + /* * SAS host attributes */ @@ -167,11 +270,26 @@ static int sas_host_setup(struct transport_container *tc, struct device *dev, sas_host->next_target_id = 0; sas_host->next_expander_id = 0; sas_host->next_port_id = 0; + + if (sas_bsg_initialize(shost, NULL)) + dev_printk(KERN_ERR, dev, "fail to a bsg device %d\n", + shost->host_no); + + return 0; +} + +static int sas_host_remove(struct transport_container *tc, struct device *dev, + struct class_device *cdev) +{ + struct Scsi_Host *shost = dev_to_shost(dev); + + sas_bsg_remove(shost, NULL); + return 0; } static DECLARE_TRANSPORT_CLASS(sas_host_class, - "sas_host", sas_host_setup, NULL, NULL); + "sas_host", sas_host_setup, sas_host_remove, NULL); static int sas_host_match(struct attribute_container *cont, struct device *dev) @@ -1287,6 +1405,9 @@ int sas_rphy_add(struct sas_rphy *rphy) return error; transport_add_device(&rphy->dev); transport_configure_device(&rphy->dev); + if (sas_bsg_initialize(shost, rphy)) + printk("fail to a bsg device %s\n", rphy->dev.bus_id); + mutex_lock(&sas_host->lock); list_add_tail(&rphy->list, &sas_host->rphy_list); @@ -1329,6 +1450,8 @@ void sas_rphy_free(struct sas_rphy *rphy) list_del(&rphy->list); mutex_unlock(&sas_host->lock); + sas_bsg_remove(shost, rphy); + transport_destroy_device(dev); put_device(dev); diff --git a/drivers/scsi/seagate.c b/drivers/scsi/seagate.c index ff62e9708e1..ce80fa9ad81 100644 --- a/drivers/scsi/seagate.c +++ b/drivers/scsi/seagate.c @@ -420,7 +420,7 @@ static inline void borken_wait (void) #define ULOOP( i ) for (clock = i*8;;) #define TIMEOUT (!(clock--)) -int __init seagate_st0x_detect (struct scsi_host_template * tpnt) +static int __init seagate_st0x_detect (struct scsi_host_template * tpnt) { struct Scsi_Host *instance; int i, j; diff --git a/drivers/scsi/sim710.c b/drivers/scsi/sim710.c index 710f19de3d4..d63d229e232 100644 --- a/drivers/scsi/sim710.c +++ b/drivers/scsi/sim710.c @@ -138,6 +138,7 @@ sim710_probe_common(struct device *dev, unsigned long base_addr, goto out_put_host; } + dev_set_drvdata(dev, host); scsi_scan_host(host); return 0; @@ -155,7 +156,7 @@ sim710_probe_common(struct device *dev, unsigned long base_addr, static __devexit int sim710_device_remove(struct device *dev) { - struct Scsi_Host *host = dev_to_shost(dev); + struct Scsi_Host *host = dev_get_drvdata(dev); struct NCR_700_Host_Parameters *hostdata = (struct NCR_700_Host_Parameters *)host->hostdata[0]; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 5143c899084..e7b6a7fde1c 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -175,7 +175,7 @@ static void scsi_cd_put(struct scsi_cd *cd) * an inode for that to work, and we do not always have one. */ -int sr_media_change(struct cdrom_device_info *cdi, int slot) +static int sr_media_change(struct cdrom_device_info *cdi, int slot) { struct scsi_cd *cd = cdi->handle; int retval; diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index fa4e08e508a..b92ff047af3 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c @@ -89,6 +89,8 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> +#include <asm/irq.h> + #include "wd33c93.h" #define optimum_sx_per(hostdata) (hostdata)->sx_table[1].period_ns @@ -1762,7 +1764,7 @@ static char setup_buffer[SETUP_BUFFER_SIZE]; static char setup_used[MAX_SETUP_ARGS]; static int done_setup = 0; -int +static int wd33c93_setup(char *str) { int i; diff --git a/drivers/scsi/zorro7xx.c b/drivers/scsi/zorro7xx.c index 50703877a58..c822debc266 100644 --- a/drivers/scsi/zorro7xx.c +++ b/drivers/scsi/zorro7xx.c @@ -130,6 +130,7 @@ static int __devinit zorro7xx_init_one(struct zorro_dev *z, goto out_put_host; } + zorro_set_drvdata(z, host); scsi_scan_host(host); return 0; @@ -148,7 +149,7 @@ static int __devinit zorro7xx_init_one(struct zorro_dev *z, static __devexit void zorro7xx_remove_one(struct zorro_dev *z) { - struct Scsi_Host *host = dev_to_shost(&z->dev); + struct Scsi_Host *host = zorro_get_drvdata(z); struct NCR_700_Host_Parameters *hostdata = shost_priv(host); scsi_remove_host(host); diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index e42faa4e428..dc1967176fe 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -1114,8 +1114,8 @@ static int __init imx_serial_init(void) static void __exit imx_serial_exit(void) { - uart_unregister_driver(&imx_reg); platform_driver_unregister(&serial_imx_driver); + uart_unregister_driver(&imx_reg); } module_init(imx_serial_init); diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c index 10bc0209cd6..3f26c4b2f32 100644 --- a/drivers/serial/s3c2410.c +++ b/drivers/serial/s3c2410.c @@ -78,7 +78,7 @@ #include <asm/hardware.h> -#include <asm/arch/regs-serial.h> +#include <asm/plat-s3c/regs-serial.h> #include <asm/arch/regs-gpio.h> /* structures */ diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index b09c83568c1..1cb56f2d5c8 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -138,7 +138,7 @@ exit: static int appledisplay_bl_update_status(struct backlight_device *bd) { - struct appledisplay *pdata = class_get_devdata(&bd->class_dev); + struct appledisplay *pdata = bl_get_data(bd); int retval; pdata->msgdata[0] = 0x10; @@ -159,7 +159,7 @@ static int appledisplay_bl_update_status(struct backlight_device *bd) static int appledisplay_bl_get_brightness(struct backlight_device *bd) { - struct appledisplay *pdata = class_get_devdata(&bd->class_dev); + struct appledisplay *pdata = bl_get_data(bd); int retval; retval = usb_control_msg( diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 2a237f09ee5..564cc9b5182 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -12,6 +12,13 @@ config VGASTATE tristate default n +config VIDEO_OUTPUT_CONTROL + tristate "Lowlevel video output switch controls" + default m + help + This framework adds support for low-level control of the video + output switch. + config FB tristate "Support for frame buffer devices" ---help--- diff --git a/drivers/video/Makefile b/drivers/video/Makefile index a562f9d69d2..518933d4905 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -123,3 +123,6 @@ obj-$(CONFIG_FB_OF) += offb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o + +#video output switch sysfs driver +obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 7fea4d8ae8e..cfcbe37d2d7 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -1733,7 +1733,7 @@ static int aty128_bl_get_level_brightness(struct aty128fb_par *par, static int aty128_bl_update_status(struct backlight_device *bd) { - struct aty128fb_par *par = class_get_devdata(&bd->class_dev); + struct aty128fb_par *par = bl_get_data(bd); unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL); int level; diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 13990697b5c..bc6f0096aa0 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -2141,7 +2141,7 @@ static int aty_bl_get_level_brightness(struct atyfb_par *par, int level) static int aty_bl_update_status(struct backlight_device *bd) { - struct atyfb_par *par = class_get_devdata(&bd->class_dev); + struct atyfb_par *par = bl_get_data(bd); unsigned int reg = aty_ld_lcd(LCD_MISC_CNTL, par); int level; diff --git a/drivers/video/aty/radeon_backlight.c b/drivers/video/aty/radeon_backlight.c index 0be25fa5540..1a056adb61c 100644 --- a/drivers/video/aty/radeon_backlight.c +++ b/drivers/video/aty/radeon_backlight.c @@ -47,7 +47,7 @@ static int radeon_bl_get_level_brightness(struct radeon_bl_privdata *pdata, static int radeon_bl_update_status(struct backlight_device *bd) { - struct radeon_bl_privdata *pdata = class_get_devdata(&bd->class_dev); + struct radeon_bl_privdata *pdata = bl_get_data(bd); struct radeonfb_info *rinfo = pdata->rinfo; u32 lvds_gen_cntl, tmpPixclksCntl; int level; @@ -206,7 +206,7 @@ void radeonfb_bl_exit(struct radeonfb_info *rinfo) if (bd) { struct radeon_bl_privdata *pdata; - pdata = class_get_devdata(&bd->class_dev); + pdata = bl_get_data(bd); backlight_device_unregister(bd); kfree(pdata); rinfo->info->bl_dev = NULL; diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index fbef663fc05..2580f5fa248 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -8,26 +8,32 @@ menuconfig BACKLIGHT_LCD_SUPPORT Enable this to be able to choose the drivers for controlling the backlight and the LCD panel on some platforms, for example on PDAs. -config BACKLIGHT_CLASS_DEVICE - tristate "Lowlevel Backlight controls" +# +# LCD +# +config LCD_CLASS_DEVICE + tristate "Lowlevel LCD controls" depends on BACKLIGHT_LCD_SUPPORT default m help - This framework adds support for low-level control of the LCD - backlight. This includes support for brightness and power. + This framework adds support for low-level control of LCD. + Some framebuffer devices connect to platform-specific LCD modules + in order to have a platform-specific way to control the flat panel + (contrast and applying power to the LCD (not to the backlight!)). To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. -config LCD_CLASS_DEVICE - tristate "Lowlevel LCD controls" +# +# Backlight +# +config BACKLIGHT_CLASS_DEVICE + tristate "Lowlevel Backlight controls" depends on BACKLIGHT_LCD_SUPPORT default m help - This framework adds support for low-level control of LCD. - Some framebuffer devices connect to platform-specific LCD modules - in order to have a platform-specific way to control the flat panel - (contrast and applying power to the LCD (not to the backlight!)). + This framework adds support for low-level control of the LCD + backlight. This includes support for brightness and power. To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 7e06223bca9..b26de8cf311 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -69,18 +69,20 @@ static inline void backlight_unregister_fb(struct backlight_device *bd) } #endif /* CONFIG_FB */ -static ssize_t backlight_show_power(struct class_device *cdev, char *buf) +static ssize_t backlight_show_power(struct device *dev, + struct device_attribute *attr,char *buf) { - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); return sprintf(buf, "%d\n", bd->props.power); } -static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, size_t count) +static ssize_t backlight_store_power(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { int rc = -ENXIO; char *endp; - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); int power = simple_strtoul(buf, &endp, 0); size_t size = endp - buf; @@ -101,18 +103,20 @@ static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, return rc; } -static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf) +static ssize_t backlight_show_brightness(struct device *dev, + struct device_attribute *attr, char *buf) { - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); return sprintf(buf, "%d\n", bd->props.brightness); } -static ssize_t backlight_store_brightness(struct class_device *cdev, const char *buf, size_t count) +static ssize_t backlight_store_brightness(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { int rc = -ENXIO; char *endp; - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); int brightness = simple_strtoul(buf, &endp, 0); size_t size = endp - buf; @@ -138,18 +142,19 @@ static ssize_t backlight_store_brightness(struct class_device *cdev, const char return rc; } -static ssize_t backlight_show_max_brightness(struct class_device *cdev, char *buf) +static ssize_t backlight_show_max_brightness(struct device *dev, + struct device_attribute *attr, char *buf) { - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); return sprintf(buf, "%d\n", bd->props.max_brightness); } -static ssize_t backlight_show_actual_brightness(struct class_device *cdev, - char *buf) +static ssize_t backlight_show_actual_brightness(struct device *dev, + struct device_attribute *attr, char *buf) { int rc = -ENXIO; - struct backlight_device *bd = to_backlight_device(cdev); + struct backlight_device *bd = to_backlight_device(dev); mutex_lock(&bd->ops_lock); if (bd->ops && bd->ops->get_brightness) @@ -159,31 +164,22 @@ static ssize_t backlight_show_actual_brightness(struct class_device *cdev, return rc; } -static void backlight_class_release(struct class_device *dev) +struct class *backlight_class; + +static void bl_device_release(struct device *dev) { struct backlight_device *bd = to_backlight_device(dev); kfree(bd); } -static struct class backlight_class = { - .name = "backlight", - .release = backlight_class_release, -}; - -#define DECLARE_ATTR(_name,_mode,_show,_store) \ -{ \ - .attr = { .name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ -} - -static const struct class_device_attribute bl_class_device_attributes[] = { - DECLARE_ATTR(power, 0644, backlight_show_power, backlight_store_power), - DECLARE_ATTR(brightness, 0644, backlight_show_brightness, +static struct device_attribute bl_device_attributes[] = { + __ATTR(bl_power, 0644, backlight_show_power, backlight_store_power), + __ATTR(brightness, 0644, backlight_show_brightness, backlight_store_brightness), - DECLARE_ATTR(actual_brightness, 0444, backlight_show_actual_brightness, + __ATTR(actual_brightness, 0444, backlight_show_actual_brightness, NULL), - DECLARE_ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL), + __ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL), + __ATTR_NULL, }; /** @@ -191,22 +187,20 @@ static const struct class_device_attribute bl_class_device_attributes[] = { * backlight_device class. * @name: the name of the new object(must be the same as the name of the * respective framebuffer device). - * @devdata: an optional pointer to be stored in the class_device. The - * methods may retrieve it by using class_get_devdata(&bd->class_dev). + * @devdata: an optional pointer to be stored for private driver use. The + * methods may retrieve it by using bl_get_data(bd). * @ops: the backlight operations structure. * - * Creates and registers new backlight class_device. Returns either an + * Creates and registers new backlight device. Returns either an * ERR_PTR() or a pointer to the newly allocated device. */ struct backlight_device *backlight_device_register(const char *name, - struct device *dev, - void *devdata, - struct backlight_ops *ops) + struct device *parent, void *devdata, struct backlight_ops *ops) { - int i, rc; struct backlight_device *new_bd; + int rc; - pr_debug("backlight_device_alloc: name=%s\n", name); + pr_debug("backlight_device_register: name=%s\n", name); new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL); if (!new_bd) @@ -214,13 +208,14 @@ struct backlight_device *backlight_device_register(const char *name, mutex_init(&new_bd->update_lock); mutex_init(&new_bd->ops_lock); - new_bd->ops = ops; - new_bd->class_dev.class = &backlight_class; - new_bd->class_dev.dev = dev; - strlcpy(new_bd->class_dev.class_id, name, KOBJ_NAME_LEN); - class_set_devdata(&new_bd->class_dev, devdata); - rc = class_device_register(&new_bd->class_dev); + new_bd->dev.class = backlight_class; + new_bd->dev.parent = parent; + new_bd->dev.release = bl_device_release; + strlcpy(new_bd->dev.bus_id, name, BUS_ID_SIZE); + dev_set_drvdata(&new_bd->dev, devdata); + + rc = device_register(&new_bd->dev); if (rc) { kfree(new_bd); return ERR_PTR(rc); @@ -228,23 +223,11 @@ struct backlight_device *backlight_device_register(const char *name, rc = backlight_register_fb(new_bd); if (rc) { - class_device_unregister(&new_bd->class_dev); + device_unregister(&new_bd->dev); return ERR_PTR(rc); } - - for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) { - rc = class_device_create_file(&new_bd->class_dev, - &bl_class_device_attributes[i]); - if (rc) { - while (--i >= 0) - class_device_remove_file(&new_bd->class_dev, - &bl_class_device_attributes[i]); - class_device_unregister(&new_bd->class_dev); - /* No need to kfree(new_bd) since release() method was called */ - return ERR_PTR(rc); - } - } + new_bd->ops = ops; #ifdef CONFIG_PMAC_BACKLIGHT mutex_lock(&pmac_backlight_mutex); @@ -265,42 +248,40 @@ EXPORT_SYMBOL(backlight_device_register); */ void backlight_device_unregister(struct backlight_device *bd) { - int i; - if (!bd) return; - pr_debug("backlight_device_unregister: name=%s\n", bd->class_dev.class_id); - #ifdef CONFIG_PMAC_BACKLIGHT mutex_lock(&pmac_backlight_mutex); if (pmac_backlight == bd) pmac_backlight = NULL; mutex_unlock(&pmac_backlight_mutex); #endif - - for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) - class_device_remove_file(&bd->class_dev, - &bl_class_device_attributes[i]); - mutex_lock(&bd->ops_lock); bd->ops = NULL; mutex_unlock(&bd->ops_lock); backlight_unregister_fb(bd); - - class_device_unregister(&bd->class_dev); + device_unregister(&bd->dev); } EXPORT_SYMBOL(backlight_device_unregister); static void __exit backlight_class_exit(void) { - class_unregister(&backlight_class); + class_destroy(backlight_class); } static int __init backlight_class_init(void) { - return class_register(&backlight_class); + backlight_class = class_create(THIS_MODULE, "backlight"); + if (IS_ERR(backlight_class)) { + printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n", + PTR_ERR(backlight_class)); + return PTR_ERR(backlight_class); + } + + backlight_class->dev_attrs = bl_device_attributes; + return 0; } /* diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c index 1b3f6586bc9..b7904da51b2 100644 --- a/drivers/video/backlight/cr_bllcd.c +++ b/drivers/video/backlight/cr_bllcd.c @@ -202,7 +202,7 @@ static int cr_backlight_probe(struct platform_device *pdev) } crp->cr_lcd_device = lcd_device_register("cr-lcd", - &pdev->dev, + &pdev->dev, NULL, &cr_lcd_ops); if (IS_ERR(crp->cr_lcd_device)) { diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 648b53c1fde..6f652c65fae 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -61,10 +61,11 @@ static inline void lcd_unregister_fb(struct lcd_device *ld) } #endif /* CONFIG_FB */ -static ssize_t lcd_show_power(struct class_device *cdev, char *buf) +static ssize_t lcd_show_power(struct device *dev, struct device_attribute *attr, + char *buf) { int rc; - struct lcd_device *ld = to_lcd_device(cdev); + struct lcd_device *ld = to_lcd_device(dev); mutex_lock(&ld->ops_lock); if (ld->ops && ld->ops->get_power) @@ -76,11 +77,12 @@ static ssize_t lcd_show_power(struct class_device *cdev, char *buf) return rc; } -static ssize_t lcd_store_power(struct class_device *cdev, const char *buf, size_t count) +static ssize_t lcd_store_power(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { int rc = -ENXIO; char *endp; - struct lcd_device *ld = to_lcd_device(cdev); + struct lcd_device *ld = to_lcd_device(dev); int power = simple_strtoul(buf, &endp, 0); size_t size = endp - buf; @@ -100,10 +102,11 @@ static ssize_t lcd_store_power(struct class_device *cdev, const char *buf, size_ return rc; } -static ssize_t lcd_show_contrast(struct class_device *cdev, char *buf) +static ssize_t lcd_show_contrast(struct device *dev, + struct device_attribute *attr, char *buf) { int rc = -ENXIO; - struct lcd_device *ld = to_lcd_device(cdev); + struct lcd_device *ld = to_lcd_device(dev); mutex_lock(&ld->ops_lock); if (ld->ops && ld->ops->get_contrast) @@ -113,11 +116,12 @@ static ssize_t lcd_show_contrast(struct class_device *cdev, char *buf) return rc; } -static ssize_t lcd_store_contrast(struct class_device *cdev, const char *buf, size_t count) +static ssize_t lcd_store_contrast(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { int rc = -ENXIO; char *endp; - struct lcd_device *ld = to_lcd_device(cdev); + struct lcd_device *ld = to_lcd_device(dev); int contrast = simple_strtoul(buf, &endp, 0); size_t size = endp - buf; @@ -137,53 +141,45 @@ static ssize_t lcd_store_contrast(struct class_device *cdev, const char *buf, si return rc; } -static ssize_t lcd_show_max_contrast(struct class_device *cdev, char *buf) +static ssize_t lcd_show_max_contrast(struct device *dev, + struct device_attribute *attr, char *buf) { - struct lcd_device *ld = to_lcd_device(cdev); + struct lcd_device *ld = to_lcd_device(dev); return sprintf(buf, "%d\n", ld->props.max_contrast); } -static void lcd_class_release(struct class_device *dev) +struct class *lcd_class; + +static void lcd_device_release(struct device *dev) { struct lcd_device *ld = to_lcd_device(dev); kfree(ld); } -static struct class lcd_class = { - .name = "lcd", - .release = lcd_class_release, -}; - -#define DECLARE_ATTR(_name,_mode,_show,_store) \ -{ \ - .attr = { .name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ -} - -static const struct class_device_attribute lcd_class_device_attributes[] = { - DECLARE_ATTR(power, 0644, lcd_show_power, lcd_store_power), - DECLARE_ATTR(contrast, 0644, lcd_show_contrast, lcd_store_contrast), - DECLARE_ATTR(max_contrast, 0444, lcd_show_max_contrast, NULL), +static struct device_attribute lcd_device_attributes[] = { + __ATTR(lcd_power, 0644, lcd_show_power, lcd_store_power), + __ATTR(contrast, 0644, lcd_show_contrast, lcd_store_contrast), + __ATTR(max_contrast, 0444, lcd_show_max_contrast, NULL), + __ATTR_NULL, }; /** * lcd_device_register - register a new object of lcd_device class. * @name: the name of the new object(must be the same as the name of the * respective framebuffer device). - * @devdata: an optional pointer to be stored in the class_device. The - * methods may retrieve it by using class_get_devdata(ld->class_dev). + * @devdata: an optional pointer to be stored in the device. The + * methods may retrieve it by using lcd_get_data(ld). * @ops: the lcd operations structure. * - * Creates and registers a new lcd class_device. Returns either an ERR_PTR() + * Creates and registers a new lcd device. Returns either an ERR_PTR() * or a pointer to the newly allocated device. */ -struct lcd_device *lcd_device_register(const char *name, void *devdata, - struct lcd_ops *ops) +struct lcd_device *lcd_device_register(const char *name, struct device *parent, + void *devdata, struct lcd_ops *ops) { - int i, rc; struct lcd_device *new_ld; + int rc; pr_debug("lcd_device_register: name=%s\n", name); @@ -193,12 +189,14 @@ struct lcd_device *lcd_device_register(const char *name, void *devdata, mutex_init(&new_ld->ops_lock); mutex_init(&new_ld->update_lock); - new_ld->ops = ops; - new_ld->class_dev.class = &lcd_class; - strlcpy(new_ld->class_dev.class_id, name, KOBJ_NAME_LEN); - class_set_devdata(&new_ld->class_dev, devdata); - rc = class_device_register(&new_ld->class_dev); + new_ld->dev.class = lcd_class; + new_ld->dev.parent = parent; + new_ld->dev.release = lcd_device_release; + strlcpy(new_ld->dev.bus_id, name, BUS_ID_SIZE); + dev_set_drvdata(&new_ld->dev, devdata); + + rc = device_register(&new_ld->dev); if (rc) { kfree(new_ld); return ERR_PTR(rc); @@ -206,22 +204,11 @@ struct lcd_device *lcd_device_register(const char *name, void *devdata, rc = lcd_register_fb(new_ld); if (rc) { - class_device_unregister(&new_ld->class_dev); + device_unregister(&new_ld->dev); return ERR_PTR(rc); } - for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++) { - rc = class_device_create_file(&new_ld->class_dev, - &lcd_class_device_attributes[i]); - if (rc) { - while (--i >= 0) - class_device_remove_file(&new_ld->class_dev, - &lcd_class_device_attributes[i]); - class_device_unregister(&new_ld->class_dev); - /* No need to kfree(new_ld) since release() method was called */ - return ERR_PTR(rc); - } - } + new_ld->ops = ops; return new_ld; } @@ -235,33 +222,34 @@ EXPORT_SYMBOL(lcd_device_register); */ void lcd_device_unregister(struct lcd_device *ld) { - int i; - if (!ld) return; - pr_debug("lcd_device_unregister: name=%s\n", ld->class_dev.class_id); - - for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++) - class_device_remove_file(&ld->class_dev, - &lcd_class_device_attributes[i]); - mutex_lock(&ld->ops_lock); ld->ops = NULL; mutex_unlock(&ld->ops_lock); lcd_unregister_fb(ld); - class_device_unregister(&ld->class_dev); + + device_unregister(&ld->dev); } EXPORT_SYMBOL(lcd_device_unregister); static void __exit lcd_class_exit(void) { - class_unregister(&lcd_class); + class_destroy(lcd_class); } static int __init lcd_class_init(void) { - return class_register(&lcd_class); + lcd_class = class_create(THIS_MODULE, "lcd"); + if (IS_ERR(lcd_class)) { + printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n", + PTR_ERR(lcd_class)); + return PTR_ERR(lcd_class); + } + + lcd_class->dev_attrs = lcd_device_attributes; + return 0; } /* diff --git a/drivers/video/nvidia/nv_backlight.c b/drivers/video/nvidia/nv_backlight.c index 43f62d8ee41..443e3c85a9a 100644 --- a/drivers/video/nvidia/nv_backlight.c +++ b/drivers/video/nvidia/nv_backlight.c @@ -50,7 +50,7 @@ static int nvidia_bl_get_level_brightness(struct nvidia_par *par, static int nvidia_bl_update_status(struct backlight_device *bd) { - struct nvidia_par *par = class_get_devdata(&bd->class_dev); + struct nvidia_par *par = bl_get_data(bd); u32 tmp_pcrt, tmp_pmc, fpcontrol; int level; diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 41381e61832..5c47968e7f2 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -307,7 +307,7 @@ static int riva_bl_get_level_brightness(struct riva_par *par, static int riva_bl_update_status(struct backlight_device *bd) { - struct riva_par *par = class_get_devdata(&bd->class_dev); + struct riva_par *par = bl_get_data(bd); U032 tmp_pcrt, tmp_pmc; int level; |